Compare commits

..

26 Commits

Author SHA1 Message Date
Omer 45268fff0d feat(rspack): respect deleteOutputPath option in rspack executor (#32609)
- Add deleteOutputPath option to rspack executor schema
- Modify rspack executor to only clean output directory when
deleteOutputPath is not explicitly set to false
- This aligns rspack executor behavior with webpack executor and allows
users to control output cleaning
- Fixes issue where rspack.output.clean configuration was being bypassed

Fixes #32015

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #

---------

Co-authored-by: Colum Ferry <cferry09@gmail.com>
2025-09-11 09:19:14 -04:00
Colum Ferry ae9b9e95d1 feat(docker): add env var for providing docker registry (#32676)
## Current Behavior
There is no method for overriding the repo config for Container Registry
and Repository Name per environment.

## Expected Behavior
Allow an environment variable `NX_DOCKER_IMAGE_REF` to be set to modify
the full image reference

---------

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2025-09-11 09:19:14 -04:00
MaxKless 875be9e1fc fix(core): detect vscode insiders as separate editor (#32679) 2025-09-11 09:19:14 -04:00
Jason Jean b8f8f2e8f0 fix(core): move git utilities to fix WASM build (#32695)
## Summary
- Moved git utilities that depend on `ignore-files` crate from
`utils/git.rs` to `watch/git_utils.rs`
- Made `find_git_root` and `collect_workspace_gitignores` functions
private since they're only used internally
- Fixed WASM build failure caused by `ignore-files` dependency not being
available for WASM targets

## Test plan
- [x] Verified `pnpm build:wasm` now succeeds
- [x] Verified regular `nx build nx` still works
- [x] Confirmed watch functionality remains intact

The `ignore-files` crate is conditionally excluded from WASM builds in
Cargo.toml, but the git utilities were trying to import it
unconditionally. Moving these utilities to the watch module (which is
already excluded from WASM) provides a cleaner solution than conditional
compilation.
2025-09-11 09:19:14 -04:00
Jason Jean 5a88b5202e chore(core): remove unused import (#32691)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

Import is unused

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

Import is removed

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2025-09-10 21:32:06 -04:00
Craigory Coppola e225d2eeca chore(repo): fixup rust setup action after pinning (#32692)
This pull request makes minor updates to the GitHub Actions workflow
configuration for Rust toolchain installation. The main change is the
addition of an explicit (empty) `rustflags` parameter to the
`actions-rust-lang/setup-rust-toolchain` steps in both the CI and
publish workflows, and a correction of the `targets` parameter to
`target` in the publish workflow.

* CI workflow updates:
* Added `rustflags: ''` to the Rust toolchain setup steps in
`.github/workflows/ci.yml` to ensure no custom Rust flags are set during
installation.
[[1]](diffhunk://#diff-b803fcb7f17ed9235f1e5cb1fcd2f5d3b2838429d4368ae4c57ce4436577f03fR72-R73)
[[2]](diffhunk://#diff-b803fcb7f17ed9235f1e5cb1fcd2f5d3b2838429d4368ae4c57ce4436577f03fR277-R278)

* Publish workflow updates:
* Changed `targets` to `target` and added `rustflags: ''` for the Rust
toolchain setup in `.github/workflows/publish.yml` to fix parameter
usage and clarify Rust flags.
2025-09-10 21:32:06 -04:00
Jason Jean 419e2c1fe9 fix(core): filter task duration estimation by successful tasks only (#32688)
## Current Behavior

Task duration estimation includes all task runs regardless of their
status (success, failure, cancelled), which can lead to inaccurate
timing predictions.

## Expected Behavior

Task duration estimation should only consider successful task runs to
provide more accurate timing estimates for future task execution
planning.

## Related Issue(s)

Fixes inaccurate task duration estimation by filtering out
failed/cancelled tasks from the calculation.

## Changes Made

- Modified SQL query in `get_estimated_task_timings` to filter by
`status = 'success'`
- Added database index on `status` column to improve query performance
- Updated both the WHERE clause condition and table schema
initialization

(cherry picked from commit bc35921690)
2025-09-10 17:21:02 -04:00
Jason Jean 6575b808c0 chore(core): remove unused import (#32689)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

The import is unused

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

The import is removed
## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #

(cherry picked from commit 3d6e304580)
2025-09-10 17:18:53 -04:00
Craigory Coppola 2cec54f234 chore(repo): turn off pm cache from setup-node in pr-title-validation (#32684)
This pull request updates the workflow configuration for PR title
validation to simplify the setup and improve caching behavior.

Workflow configuration updates:

* Removed the explicit installation step for `pnpm` using
`pnpm/action-setup`, as this workflow doesn't install packages
* Updated the Node.js setup step to disable package manager caching by
setting `package-manager-cache: false`, which replaces the previous
`cache: 'pnpm'` option.…. as this workflow doesn't install packages

(cherry picked from commit b582e1f761)
2025-09-10 17:18:53 -04:00
Craigory Coppola 05df41a9d4 chore(repo): add CI check to prevent tracked files being listed in .gitignore (#32460)
> [!NOTE]
> CI is failing on this PR right now, its expected. We don't currently
adhere to this advice, and its causing issues. This PR should represent
a sync of this, and future protection

## Current Behavior

Previously, we had no automated check to detect when files tracked by
Git were inadvertently listed in `.gitignore`. This could lead to
inconsistent behavior in Nx's caching system.

## Expected Behavior

With this PR, the CI pipeline now includes a check
(`check-git-ignored-tracked-files`) that identifies tracked files that
would be ignored by Git if they weren't already being tracked. This
ensures consistency in Nx's cache hash calculation.

## Why This Matters for Nx

This check is critical for Nx's caching system because:

**Hash Calculation Consistency**: Nx calculates cache hashes based on
the files that Git tracks. When a file is both tracked by Git and listed
in `.gitignore`, it creates an inconsistency where:
- Git includes the file in operations (and Nx hash calculations)
- `.gitignore` indicates the file should be ignored
- This can lead to different hash calculations depending on timing and
environment

**Impact on Cache Performance**:
- **Cache Misses**: Valid cache entries may be missed due to hash
discrepancies
- **Unreliable Builds**: Tasks may not re-run when they should, or may
re-run unnecessarily
- **Cross-Environment Issues**: Different developers or CI environments
may calculate different hashes for identical code

**The Solution**: The new script identifies these problematic files and
provides clear remediation steps:
- For files that shouldn't be tracked: `git rm --cached <file>`
- For files that should be tracked: Update `.gitignore` to be more
specific

This ensures that Nx's intelligent caching system works as designed,
providing fast, reliable builds across the entire development workflow.

## Related Issue(s)

Fixes the inconsistency between Git tracking and ignore patterns that
can affect Nx cache hash calculations.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit 44b0c61a29)
2025-09-10 11:27:41 -04:00
Jack Hsu 61919be78d chore(repo): update Node e2e test to be less flaky (#32678)
This PR updates a test such that it doesn't just wait 1 second for
output, but waits until the expected output either prints or times out.

(cherry picked from commit 3a4b2752f2)
2025-09-10 11:27:40 -04:00
MaxKless b48ef15844 fix(core): handle uninstalled nx console case in autoinstall logic (#32673)
(cherry picked from commit 25c2001caf)
2025-09-10 11:27:36 -04:00
MaxKless 855b736063 fix(core): check nx packages for provenance config before running nx migrate (#32557)
(cherry picked from commit 1ff26dde52)
2025-09-10 11:27:34 -04:00
Jason Jean 416bb04599 fix(core): resolve watcher infinite loops from missing parent gitignore support (#32604)
## Current Behavior

Nx watcher ignores parent `.gitignore` files, causing infinite loops
when `.nx` folders are ignored only by parent gitignores outside
workspace root. Tasks hang indefinitely with no error messages.

## Expected Behavior

Watcher respects parent `.gitignore` files up to git repository
boundaries, preventing infinite loops.

## Changes Made

- **Fixed watcher**: Now traverses parent directories and respects
`.gitignore` files up to git root
- **Centralized logic**: New `git.rs` module eliminates duplicate, buggy
implementations
- **Path handling**: Fixed absolute vs relative path issues causing
filter failures
- **Git boundaries**: Added proper git root detection for traversal
limits

## Files Modified

- `packages/nx/src/native/utils/git.rs` - New module with git utilities
- `packages/nx/src/native/utils/mod.rs` - Export git module
- `packages/nx/src/native/walker.rs` - Use shared utilities
- `packages/nx/src/native/watch/utils.rs` - Remove duplicate functions
- `packages/nx/src/native/watch/watch_filterer.rs` - Use centralized
functions

## Related Issue(s)

Fixes #30313

---------

Co-authored-by: Claude <noreply@anthropic.com>
(cherry picked from commit 27cfc7efa1)
2025-09-10 11:27:32 -04:00
Copilot 09f63423fe fix(core): add bold styling to terminal pane title when focused (#32462)
## Current Behavior

Terminal panes in the TUI show focus state through color changes only -
focused panes display the task name in primary foreground color while
unfocused panes use secondary foreground color.

## Expected Behavior

Terminal panes should provide stronger visual feedback when focused by
making the title bold in addition to the existing color change,
consistent with other TUI components like the dependency view.

## Related Issue(s)

This enhancement improves the visual hierarchy and makes it easier for
users to identify which terminal pane currently has focus, especially in
multi-pane layouts.

## Changes Made

- Modified the title styling logic in `TerminalPane::render()` to
conditionally apply `Modifier::BOLD` when the pane is focused
- Added a test to verify the focus state behavior is correctly
implemented
- The change follows the existing pattern used in other components and
maintains backward compatibility

The implementation uses a conditional modifier that applies bold styling
only when `state.is_focused` is true, leaving unfocused panes unchanged.
This provides consistent visual feedback across the TUI while preserving
existing functionality.

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: AgentEnder <6933928+AgentEnder@users.noreply.github.com>
(cherry picked from commit c586749a93)
2025-09-10 11:27:30 -04:00
Jack Hsu 7abf56dfa5 docs(misc): update links on plugin-registry to point to intro pages when they exist (#32611)
This PR updates the the plugin registry links to point to intro since
the API docs aren't useful as a discovery page.

Since we use the API path to build generator, executor, etc. links I
added a new `introPath` property instead.

(cherry picked from commit 4299d82b9b)
2025-09-10 11:27:27 -04:00
Jack Hsu c13d300726 chore(repo): pin actions to shas (#32657)
This PR pins our publish actions to specific SHAs, and removes unused
logic tied to the previous PR release support.

Note: `dtolnay/rust-toolchain` cannot be pinned since will treat the SHA
as the Rust version. e.g. `@abc` will try to install Rust version `abc`,
which is invalid.

(cherry picked from commit 6eb1d21384)
2025-09-10 11:27:25 -04:00
Jack Hsu 8e511ca746 feat(misc): add Cookiebot global scripts to astro-docs (#32660)
Adds cookie consent management and analytics tracking to the astro-docs
project:
- Cookiebot consent script integration with COOKIEBOT_ID environment
variable
- GlobalScripts React component reused from nx-dev for consistency
- Head.astro component override to integrate scripts into Starlight
- Cookie consent categories: statistics (GA, GTM) and marketing
(HubSpot, Apollo, Hotjar, Twitter)
- Production-only script loading with COOKIEBOT_DISABLE bypass option

Fixes DOC-95

Co-authored-by: Claude <noreply@anthropic.com>
(cherry picked from commit 8fe7b0b066)
2025-09-10 11:27:20 -04:00
Jack Hsu 4a933dcb36 docs(misc): add opengraph image support for astro docs (#32636)
This PR adds `og:image` meta tags to documentation pages for the new
docs.

The images are provided for pages under `docs` and `plugin-docs`
collections, with a fallback for pages not in those collections. We can
add more in the future as well if needed.

Examples:

<img width="1200" height="630" alt="releases"
src="https://github.com/user-attachments/assets/52322296-6048-4f40-8a78-7878406aa053"
/>

<img width="1200" height="630" alt="generators"
src="https://github.com/user-attachments/assets/cb238f9f-28c6-4c3d-83aa-ee231d99d72e"
/>

---

Closes #DOC-1164

(cherry picked from commit ec78ef1928)
2025-09-10 11:27:18 -04:00
Miguel b47458a26a fix(release): optimize release version internals (#32534)
ReleaseGroupProcessor already iterates through all release groups, and
sometimes change is anyways not propagated.

## Current Behavior

All release groups are iterated in a way that, if group A depends on
group B:
- A will
[propagate](https://github.com/nrwl/nx/blob/53bb276a39a2de09938ed1161fadbbe92a96e596/packages/nx/src/command-line/release/version/release-group-processor.ts#L832)
changes to B
- B will
[check](https://github.com/nrwl/nx/blob/53bb276a39a2de09938ed1161fadbbe92a96e596/packages/nx/src/command-line/release/version/release-group-processor.ts#L894)
if it should be bumped by its dependencies

This means there is redundant double-checking in the code.

However, propagation doesn't always work. The linked
[snippet](https://github.com/nrwl/nx/blob/53bb276a39a2de09938ed1161fadbbe92a96e596/packages/nx/src/command-line/release/version/release-group-processor.ts#L1626-L1661)
selects a random project of group *A*, and if that project doesn't have
dependencies to projects in group *B*, it doesn't consider *B* as
bumped.

## Expected Behavior

### Ideally
There would be no double loops.

### Otherwise
If there are, they should consider all projects when deciding what to
bump. For instance, with something like:
```typescript
      const hasDependencyInChangedGroupV2 = Array.from(releaseGroupFilteredProjects).some(
        (project) => {
          const dependencies = this.projectGraph.dependencies[project] || [];
          return dependencies.some(
            (dep) =>
              this.getReleaseGroupNameForProject(dep.target) ===
              changedDependencyGroup
          )
      })
```

## Notes

- I know it's a bold move to simply delete code. I wanted to highlight
that, if tests pass, we are either missing tests or we are doing
unnecessary work. I would like to replicate locally with a bit more
confidence, but `jest` tests seem to be flaky
- I would need to pick @JamesHenry 's brain about what's the intention
with this "double" propagation in order to have the full context here

(cherry picked from commit 383f3aa8ce)
2025-09-10 11:27:17 -04:00
Craigory Coppola 7d1d22d381 fix(core): check if daemon process is actually alive before trying to kill it (#32661)
## Current Behavior
If the daemon shuts down, and the user tries to run `nx reset`, an ESRCH
error is thrown since the pid in server-process.json doesn't match up to
a process

## Expected Behavior
We check if the process is alive before terminating it

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #

(cherry picked from commit fdc3e39899)
2025-09-10 11:27:15 -04:00
Colum Ferry 9bddf597c0 fix(bundling): postcss-cli-resources should handle relative urls #32582 (#32658)
## Current Behavior
Relative urls are not being handled correctly in the
`postcss-cli-resources` Plugins for Webpack and Rspack after switching
to use WHATWG URL in favour of the deprecated `url.parse()` method.

## Expected Behavior
Ensure relatives are handled appropriately by resolving them based on
the context of the current resource being loaded.

## Related Issue(s)

Fixes #32582

(cherry picked from commit 95e7b49967)
2025-09-10 11:27:13 -04:00
Leosvel Pérez Espinosa 7dba5375ef fix(core): invalidate project graph when external nodes change (#32626)
## Current Behavior

When external nodes change, the project graph cache is not invalidated.
This can sometimes lead to missing dependencies due to reusing cached
dependencies for nodes that no longer exist.

## Expected Behavior

When external nodes change, the project graph cache should be
invalidated.

(cherry picked from commit 5011ecd0a1)
2025-09-10 11:27:11 -04:00
Copilot 2d43de329a fix(repo): update broken CI documentation link in README (#32633)
## Current Behavior

The README.md file contains a broken link to the CI documentation that
points to `/ci/intro`, which no longer exists.

## Expected Behavior

The README.md file should contain the correct link to the CI
documentation pointing to `/ci/getting-started/intro`.

## Related Issue(s)

Fixes #32549

Credits to @MeAkib for originally identifying and addressing this issue.

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 Share your feedback on Copilot coding agent for the chance to win a
$200 gift card! Click
[here](https://survey3.medallia.com/?EAHeSx-AP01bZqG0Ld9QLQ) to start
the survey.

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: FrozenPandaz <8104246+FrozenPandaz@users.noreply.github.com>
(cherry picked from commit f91420d7c5)
2025-09-10 11:27:10 -04:00
Colum Ferry de6de0c88d fix(core): ensure only supported bundlers are used for angular fallback to default (#32655)
## Current Behavior
If an invalid bundler is provided to `create-nx-workspace` for the
`angular-monorepo` preset, it defaults to `webpack`.

## Expected Behavior
Invalid bundler option should default to the default provided within in
the angular app generator

(cherry picked from commit ceaa58e36c)
2025-09-10 11:27:09 -04:00
Miroslav Jonaš c4d6c10d20 fix(vite): handle config server properly for libs (#32608)
## Current Behavior
The `Vite`'s `resolveConfig` always adds default server settings to the
config:

```
server: {
    preTransformRequests: true,
    sourcemapIgnoreList: [Function: isInNodeModules$1],
    middlewareMode: false,
    fs: {
      strict: true,
      allow: [Array],
      deny: [Array],
      cachedChecks: undefined
    }
  },
```

This leads to `libs` always ending up with serve targets even if we
don't define `serve` configuration

## Expected Behavior
The serve targets should only exist if we explicitly set port or host.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #

(cherry picked from commit 46c26280e6)
2025-09-10 11:27:07 -04:00
145 changed files with 2663 additions and 5670 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
"env": {
"node": true
},
"ignorePatterns": ["**/*.ts", "**/test-output"],
"ignorePatterns": ["**/*.ts"],
"plugins": ["@typescript-eslint", "@nx"],
"extends": ["plugin:storybook/recommended"],
"rules": {
-104
View File
@@ -1,104 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "master" ]
schedule:
- cron: '20 14 * * 6'
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: actions
build-mode: none
# We would like to test our Java / Kotlin... but its currently failing. We can follow up.
# - language: java-kotlin
# build-mode: autobuild
- language: javascript-typescript
build-mode: none
- language: rust
build-mode: none
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
# Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node`
# or others). This is typically only required for manual builds.
# - name: Setup runtime (example)
# uses: actions/setup-example@v1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@15403aac29bd91419968e066cded66bde56b0283 # v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@15403aac29bd91419968e066cded66bde56b0283 # v3
with:
category: "/language:${{matrix.language}}"
-101
View File
@@ -1,101 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
pull_request:
branches: [ "**" ]
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: actions
build-mode: none
# See comment in @./codeql-master.yml about Java / Kotlin
# - language: java-kotlin
# build-mode: autobuild
- language: javascript-typescript
build-mode: none
- language: rust
build-mode: none
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
# Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node`
# or others). This is typically only required for manual builds.
# - name: Setup runtime (example)
# uses: actions/setup-example@v1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@15403aac29bd91419968e066cded66bde56b0283 # v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@15403aac29bd91419968e066cded66bde56b0283 # v3
with:
category: "/language:${{matrix.language}}"
upload: 'never'
upload-database: false
-22
View File
@@ -432,28 +432,6 @@ jobs:
if: ${{ !github.event.release.prerelease && github.event_name == 'release' }}
run: npx ts-node -P ./scripts/tsconfig.scripts.json ./scripts/release-docs.ts
report-pending-publish:
name: Report Pending Publish to Slack
if: ${{ github.repository_owner == 'nrwl' }}
needs:
- resolve-required-data
- build-freebsd
- build
runs-on: ubuntu-latest
timeout-minutes: 10
continue-on-error: true # Don't fail the workflow if notification fails
steps:
- name: Send Slack notification
uses: ravsamhq/notify-slack-action@be814b201e233b2dc673608aa46e5447c8ab13f2 # v11
with:
status: ${{ job.status }}
notification_title: '📦 Publish Pending Review'
message_format: ${{ format('Version `{0}` is being published to NPM - manual review is required', needs.resolve-required-data.outputs.version) }}
footer: '<{run_url}|View Workflow Run>'
mention_groups: 'U9NPA6C90' # Jason
env:
SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
pr_failure_comment:
# Run this job if it is a PR release, running on the nrwl origin, and any of the required jobs failed
if: ${{ github.repository_owner == 'nrwl' && github.event.inputs.pr && always() && contains(needs.*.result, 'failure') }}
-2
View File
@@ -117,5 +117,3 @@ packages/angular-rspack-compiler/coverage
# Angular Rspack Packages use a template to generate the correct README
packages/angular-rspack/README.md
packages/angular-rspack-compiler/README.md
test-output
-6
View File
@@ -1,6 +0,0 @@
node_modules/
dist/
.astro/
.netlify/
test-output/
playwright-report/
-22
View File
@@ -1,22 +0,0 @@
{
"extends": ["plugin:playwright/recommended", "../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
},
{
"files": ["e2e/**/*.{ts,js,tsx,jsx}"],
"rules": {}
}
]
}
-1
View File
@@ -86,7 +86,6 @@ export default defineConfig({
// since the sidebar doesn't auto generate w/ dynamic routes from src/pages/reference
// only the src/content/docs/reference files
'./src/plugins/sidebar-reference-updater.middleware.ts',
'./src/plugins/sidebar-icons.middleware.ts',
'./src/plugins/og.middleware.ts',
],
markdown: {
@@ -1,45 +0,0 @@
import { test, expect } from '@playwright/test';
test('links in descriptions of properties should correctly link to the same page w/ url fragments', async ({
page,
}) => {
await page.goto('/docs/reference/devkit/NxJsonConfiguration');
await expect(
page.getByRole('heading', { name: 'NxJsonConfiguration' })
).toBeVisible();
await page
.getByTestId('main-pane')
.getByRole('link', { name: 'nxCloudAccessToken' })
.click();
await expect(
page.getByRole('heading', { name: 'nxCloudAccessToken' })
).toBeVisible();
const description = page
.getByRole('paragraph')
.filter({ has: page.getByRole('link', { name: 'tasksRunnerOptions' }) })
.first();
await expect(description).toBeVisible();
const linkedProperty = description.getByRole('link', {
name: 'tasksRunnerOptions',
});
await expect(linkedProperty).toBeVisible();
await expect(linkedProperty).toHaveAttribute(
'href',
'/docs/reference/devkit/NxJsonConfiguration#tasksrunneroptions'
);
await linkedProperty.click();
expect(page.url()).toContain('#tasksrunneroptions');
await expect(
page.getByRole('heading', { name: 'tasksRunnerOptions' })
).toBeVisible();
});
-35
View File
@@ -1,35 +0,0 @@
const url = 'http://localhost:4321/docs';
const timeout = 120000; // 2 minutes in milliseconds
console.log('starting up....');
export default async function globalSetup() {
const startTime = Date.now();
const maxEndTime = startTime + timeout;
console.log(`Waiting for ${url} to be available...`);
while (Date.now() < maxEndTime) {
try {
const response = await fetch(url);
if (response.ok) {
console.log(`✓ Server is ready at ${url}`);
return;
}
console.log(
`Server responded with status ${response.status}, retrying...`
);
} catch (error) {
// Server not available yet, continue polling
const remainingTime = Math.round((maxEndTime - Date.now()) / 1000);
if (remainingTime % 10 === 0 && remainingTime > 0) {
console.log(`Still waiting... ${remainingTime} seconds remaining`);
}
}
await new Promise((resolve) => setTimeout(resolve, 1000));
}
throw new Error(
`Server at ${url} did not become available within ${timeout / 1000} seconds`
);
}
+1 -7
View File
@@ -27,13 +27,7 @@ export default defineMarkdocConfig({
type: 'String',
required: false,
default: 'default',
matches: [
'default',
'gradient',
'inverted',
'gradient-alt',
'simple',
],
matches: ['default', 'gradient', 'inverted', 'gradient-alt'],
},
size: {
type: 'String',
-42
View File
@@ -1,42 +0,0 @@
import { defineConfig, devices } from '@playwright/test';
import { nxE2EPreset } from '@nx/playwright/preset';
import { workspaceRoot } from '@nx/devkit';
import { join } from 'path';
// For CI, you may want to set BASE_URL to the deployed application.
const baseURL = process.env['BASE_URL'] || 'http://localhost:4321';
const reportDir = join(
workspaceRoot,
'dist',
'astro-docs',
'playwright-report'
);
export default defineConfig({
...nxE2EPreset(__filename, { testDir: './e2e' }),
reporter: [
['list', { printSteps: true }],
['html', { outputFolder: reportDir, open: 'never' }],
[
'junit',
{
// JUnit only respects the outputFile option, and not outputDir or outputFolder
outputFile: `${reportDir}/test-e2e-nx-cloud.xml`,
},
],
],
/* Global setup to wait for server */
globalSetup: require.resolve('./global-setup.e2e.ts'),
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
baseURL,
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
});
-12
View File
@@ -51,20 +51,8 @@
"dependsOn": ["validate-links"],
"command": "echo done"
},
"pw-e2e": {
"dependsOn": ["serve"],
"parallelism": true
},
"e2e-ci--**/*": {
"dependsOn": ["preview"],
"parallelism": true
},
"show-report": {
"command": "playwright show-report dist/astro-docs/playwright-report"
},
"validate-links": {
"dependsOn": ["build"],
"cache": true,
"inputs": [
"{projectRoot}/src/**/*",
"{projectRoot}/astro.config.mjs",
@@ -1 +0,0 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Angular</title><path d="M16.712 17.711H7.288l-1.204 2.916L12 24l5.916-3.373-1.204-2.916ZM14.692 0l7.832 16.855.814-12.856L14.692 0ZM9.308 0 .662 3.999l.814 12.856L9.308 0Zm-.405 13.93h6.198L12 6.396 8.903 13.93Z"/></svg>

Before

Width:  |  Height:  |  Size: 299 B

@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 7.5-9-5.25L3 7.5m18 0-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9" />
</svg>

Before

Width:  |  Height:  |  Size: 284 B

@@ -1 +0,0 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>ESLint</title><path d="M7.257 9.132L11.816 6.5a.369.369 0 0 1 .368 0l4.559 2.632a.369.369 0 0 1 .184.32v5.263a.37.37 0 0 1-.184.319l-4.559 2.632a.369.369 0 0 1-.368 0l-4.559-2.632a.369.369 0 0 1-.184-.32V9.452a.37.37 0 0 1 .184-.32M23.852 11.53l-5.446-9.475c-.198-.343-.564-.596-.96-.596H6.555c-.396 0-.762.253-.96.596L.149 11.509a1.127 1.127 0 0 0 0 1.117l5.447 9.398c.197.342.563.517.959.517h10.893c.395 0 .76-.17.959-.512l5.446-9.413a1.069 1.069 0 0 0 0-1.086m-4.51 4.556a.4.4 0 0 1-.204.338L12.2 20.426a.395.395 0 0 1-.392 0l-6.943-4.002a.4.4 0 0 1-.205-.338V8.08c0-.14.083-.269.204-.338L11.8 3.74c.12-.07.272-.07.392 0l6.943 4.003a.4.4 0 0 1 .206.338z"/></svg>

Before

Width:  |  Height:  |  Size: 743 B

-9
View File
@@ -1,9 +0,0 @@
<svg
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
>
<title>Java</title>
<path d="m16.5093 4.9869-.0607-.0347c-1.1014.369-4.4915 1.707-4.4915 4.202 0 1.411 1.378 2.1925 1.378 3.5158 0 .472-.2666.9146-.4836 1.1791l.1091.063c.5735-.3728 1.589-1.18 1.589-2.2222 0-.8825-1.2216-1.943-1.2216-3.0774 0-1.7875 2.357-3.1899 3.1813-3.6256zm-1.6642-3.27c0 3.6925-5.0604 5.1055-5.0604 7.7309 0 1.843 1.2222 2.9987 1.8983 3.7293l-.055.0317c-.8536-.534-3.0995-1.876-3.0995-4.0927 0-3.112 5.8123-4.599 5.8123-8.134 0-.435-.0644-.7683-.1095-.9482L14.2901 0c.1842.2315.555.8102.555 1.7168m.514 14.9392c-.5962.1688-1.9389.4441-3.859.4441-1.8844 0-3.424-.3226-3.4289-.7024-.0032-.2527.3024-.3628.3024-.3628l-.0544-.0316c-.9023.1595-1.7406.406-1.7357.7752.0084.6698 2.5697 1.1728 4.9129 1.1728 1.992 0 3.9053-.3343 4.7684-.7722zm-6.368 2.0708c-.4185.0832-1.3305.2926-1.3305.7361 0 .6144 1.9513 1.085 3.835 1.085 2.5922 0 3.6539-.667 3.702-.7016l-1.078-.6235c-.4583.1092-1.2306.2808-2.6214.2808-1.5521 0-2.5634-.2658-2.5634-.5569 0-.0617.0386-.135.1106-.1886zm10.5923-4.1337c-.0725 1.3911-1.3577 2.2573-2.6423 2.9893l.1164.067c1.3708-.3855 3.8166-1.5085 3.6144-3.2346-.1007-.8608-.8875-1.4758-1.9133-1.4758-.3194 0-.6036.0563-.834.1265l-.0007.0022-.0486.1224c.9175-.1796 1.7558.4904 1.7082 1.403zm-8.1232 8.1638c3.6058-.0313 7.6402-.737 7.6298-1.9232-.0019-.215-.1418-.3622-.2635-.4513l-.0592.034c-.3333.9188-3.1508 1.5977-7.3132 1.634-2.6859.0234-6.4063-.62-6.4128-1.3636-.0065-.7455 1.7625-1.1552 1.7625-1.1552l-.125-.0714c-1.1854.1632-3.3697.731-3.3625 1.5506.0104 1.185 5.0304 1.7734 8.1439 1.7461zm-.375.847c-1.4333.0126-3.1833-.1061-4.6555-.3535l-.1363.0784c1.4664.43 3.508.6896 5.7514.6702 4.4059-.0386 7.9779-1.1311 8.0485-2.446l-.051-.0297c-.2953.3604-2.2009 2.0216-8.9572 2.0806zm-5.5195-9.328c0-.6646 2.521-1.0374 3.6947-1.1277l.112.0647c-.451.082-2.26.401-2.26.8172 0 .4532 2.7747.7501 4.3853.7501 2.7355 0 4.595-.414 5.095-.5505l.6997.4072c-.4792.2346-2.5362.8495-5.7945.8495-3.6214 0-5.9322-.7086-5.9322-1.2105" />
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

@@ -1,18 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1244_718)">
<path d="M9.35205 3.72154V7.35041L12.0002 8.93803M9.35205 3.72154L12.0002 2.13391L14.6483 3.72154M9.35205 3.72154L12.0002 5.08236M12.0002 8.93803L14.6483 7.35041V3.72154M12.0002 8.93803V5.08236M14.6483 3.72154L12.0002 5.08236" stroke="black" stroke-width="0.5" stroke-linejoin="round"/>
<path d="M8.3335 3.26804L12.0002 1L15.6668 3.26804V7.80412L12.0002 10.0722L8.3335 7.80412V3.26804Z" stroke="black" stroke-width="0.5" stroke-linejoin="round"/>
<path d="M2.01855 15.2888V18.9177L4.6667 20.5053M2.01855 15.2888L4.6667 13.7012L7.31485 15.2888M2.01855 15.2888L4.6667 16.8764M4.6667 20.5053L7.31485 18.9177V15.2888M4.6667 20.5053V16.8764M7.31485 15.2888L4.6667 16.8764" stroke="black" stroke-width="0.5" stroke-linejoin="round"/>
<path d="M1 14.8349L4.66667 12.5669L8.33333 14.8349V19.371L4.66667 21.6391L1 19.371V14.8349Z" stroke="black" stroke-width="0.5" stroke-linejoin="round"/>
<path d="M16.6855 15.2888V18.9177L19.3337 20.5053M16.6855 15.2888L19.3337 13.7012L21.9818 15.2888M16.6855 15.2888L19.3337 16.6496M19.3337 20.5053L21.9818 18.9177V15.2888M19.3337 20.5053V16.6496M21.9818 15.2888L19.3337 16.6496" stroke="black" stroke-width="0.5" stroke-linejoin="round"/>
<path d="M15.6665 14.8349L19.3332 12.5669L22.9998 14.8349V19.371L19.3332 21.6391L15.6665 19.371V14.8349Z" stroke="black" stroke-width="0.5" stroke-linejoin="round"/>
<path d="M8.33317 6.89685L4.6665 9.16489V12.567" stroke="black" stroke-width="0.5" stroke-linejoin="round"/>
<path d="M15.6668 6.89685L19.3335 9.16489V12.567" stroke="black" stroke-width="0.5" stroke-linejoin="round"/>
<path d="M7.51855 19.8248L12 23.0001L16.4815 19.8248" stroke="black" stroke-width="0.5" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_1244_718">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

@@ -1 +0,0 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Node.js</title><path d="M11.998,24c-0.321,0-0.641-0.084-0.922-0.247l-2.936-1.737c-0.438-0.245-0.224-0.332-0.08-0.383 c0.585-0.203,0.703-0.25,1.328-0.604c0.065-0.037,0.151-0.023,0.218,0.017l2.256,1.339c0.082,0.045,0.197,0.045,0.272,0l8.795-5.076 c0.082-0.047,0.134-0.141,0.134-0.238V6.921c0-0.099-0.053-0.192-0.137-0.242l-8.791-5.072c-0.081-0.047-0.189-0.047-0.271,0 L3.075,6.68C2.99,6.729,2.936,6.825,2.936,6.921v10.15c0,0.097,0.054,0.189,0.139,0.235l2.409,1.392 c1.307,0.654,2.108-0.116,2.108-0.89V7.787c0-0.142,0.114-0.253,0.256-0.253h1.115c0.139,0,0.255,0.112,0.255,0.253v10.021 c0,1.745-0.95,2.745-2.604,2.745c-0.508,0-0.909,0-2.026-0.551L2.28,18.675c-0.57-0.329-0.922-0.945-0.922-1.604V6.921 c0-0.659,0.353-1.275,0.922-1.603l8.795-5.082c0.557-0.315,1.296-0.315,1.848,0l8.794,5.082c0.57,0.329,0.924,0.944,0.924,1.603 v10.15c0,0.659-0.354,1.273-0.924,1.604l-8.794,5.078C12.643,23.916,12.324,24,11.998,24z M19.099,13.993 c0-1.9-1.284-2.406-3.987-2.763c-2.731-0.361-3.009-0.548-3.009-1.187c0-0.528,0.235-1.233,2.258-1.233 c1.807,0,2.473,0.389,2.747,1.607c0.024,0.115,0.129,0.199,0.247,0.199h1.141c0.071,0,0.138-0.031,0.186-0.081 c0.048-0.054,0.074-0.123,0.067-0.196c-0.177-2.098-1.571-3.076-4.388-3.076c-2.508,0-4.004,1.058-4.004,2.833 c0,1.925,1.488,2.457,3.895,2.695c2.88,0.282,3.103,0.703,3.103,1.269c0,0.983-0.789,1.402-2.642,1.402 c-2.327,0-2.839-0.584-3.011-1.742c-0.02-0.124-0.126-0.215-0.253-0.215h-1.137c-0.141,0-0.254,0.112-0.254,0.253 c0,1.482,0.806,3.248,4.655,3.248C17.501,17.007,19.099,15.91,19.099,13.993z"/></svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

-1
View File
@@ -1 +0,0 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>React</title><path d="M14.23 12.004a2.236 2.236 0 0 1-2.235 2.236 2.236 2.236 0 0 1-2.236-2.236 2.236 2.236 0 0 1 2.235-2.236 2.236 2.236 0 0 1 2.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38-.318-.184-.688-.277-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44-.96-.236-2.006-.417-3.107-.534-.66-.905-1.345-1.727-2.035-2.447 1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442-1.107.117-2.154.298-3.113.538-.112-.49-.195-.964-.254-1.42-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87-.728.063-1.466.098-2.21.098-.74 0-1.477-.035-2.202-.093-.406-.582-.802-1.204-1.183-1.86-.372-.64-.71-1.29-1.018-1.946.303-.657.646-1.313 1.013-1.954.38-.66.773-1.286 1.18-1.868.728-.064 1.466-.098 2.21-.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933-.2-.39-.41-.783-.64-1.174-.225-.392-.465-.774-.705-1.146zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493-.28-.958-.646-1.956-1.1-2.98.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98-.45 1.017-.812 2.01-1.086 2.964-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39.24-.375.48-.762.705-1.158.225-.39.435-.788.636-1.18zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143-.695-.102-1.365-.23-2.006-.386.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295-.22-.005-.406-.05-.553-.132-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z"/></svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.75 3.104v5.714a2.25 2.25 0 0 1-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 0 1 4.5 0m0 0v5.714c0 .597.237 1.17.659 1.591L19.8 15.3M14.25 3.104c.251.023.501.05.75.082M19.8 15.3l-1.57.393A9.065 9.065 0 0 1 12 15a9.065 9.065 0 0 0-6.23-.693L5 14.5m14.8.8 1.402 1.402c1.232 1.232.65 3.318-1.067 3.611A48.309 48.309 0 0 1 12 21c-2.773 0-5.491-.235-8.135-.687-1.718-.293-2.3-2.379-1.067-3.61L5 14.5" />
</svg>

Before

Width:  |  Height:  |  Size: 632 B

@@ -1,4 +0,0 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>TypeScript</title>
<path d="M0 12v12h24V0H0zm19.341-.956c.61.152 1.074.423 1.501.865.221.236.549.666.575.77.008.03-1.036.73-1.668 1.123-.023.015-.115-.084-.217-.236-.31-.45-.633-.644-1.128-.678-.728-.05-1.196.331-1.192.967a.88.88 0 0 0 .102.45c.16.331.458.53 1.39.933 1.719.74 2.454 1.227 2.911 1.92.51.773.625 2.008.278 2.926-.38.998-1.325 1.676-2.655 1.9-.411.073-1.386.062-1.828-.018-.964-.172-1.878-.648-2.442-1.273-.221-.243-.652-.88-.625-.925.011-.016.11-.077.22-.141.108-.061.511-.294.892-.515l.69-.4.145.214c.202.308.643.731.91.872.766.404 1.817.347 2.335-.118a.883.883 0 0 0 .313-.72c0-.278-.035-.4-.18-.61-.186-.266-.567-.49-1.649-.96-1.238-.533-1.771-.864-2.259-1.39a3.165 3.165 0 0 1-.659-1.2c-.091-.339-.114-1.189-.042-1.531.255-1.197 1.158-2.03 2.461-2.278.423-.08 1.406-.05 1.821.053zm-5.634 1.002l.008.983H10.59v8.876H8.38v-8.876H5.258v-.964c0-.534.011-.98.026-.99.012-.016 1.913-.024 4.217-.02l4.195.012z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1019 B

@@ -1 +0,0 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Vue.js</title><path d="M24,1.61H14.06L12,5.16,9.94,1.61H0L12,22.39ZM12,14.08,5.16,2.23H9.59L12,6.41l2.41-4.18h4.43Z"/></svg>

Before

Width:  |  Height:  |  Size: 202 B

+2 -4
View File
@@ -13,12 +13,12 @@ export const sidebar: StarlightUserConfig['sidebar'] = [
autogenerate: { directory: 'features', collapsed: true },
},
{
label: 'Core Guides',
label: 'Guides',
collapsed: true,
autogenerate: { directory: 'guides', collapsed: true },
},
{
label: 'Core Concepts',
label: 'Concepts',
collapsed: true,
autogenerate: { directory: 'concepts', collapsed: true },
},
@@ -39,12 +39,10 @@ export const sidebar: StarlightUserConfig['sidebar'] = [
...getPluginItems('angular'),
{
label: 'Angular Rspack',
collapsed: true,
items: getPluginItems('angular-rspack', 'angular'),
},
{
label: 'Angular Rsbuild',
collapsed: true,
items: getPluginItems('angular-rsbuild', 'angular'),
},
],
+1 -22
View File
@@ -175,31 +175,10 @@ const currentVersion = versions.find(v => v.current);
{shouldRenderSearch && <Search />}
</div>
<div class="hidden md:flex items-center gap-4 print:hidden">
<!-- CTA Buttons - Hide on screens smaller than xl (1280px) -->
<div class="hidden xl:flex items-center gap-2">
<a
href="https://nx.dev/contact"
class="inline-flex items-center justify-center px-2.5 py-1.5 text-sm font-medium rounded-md transition no-underline border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-200 hover:bg-slate-50 dark:hover:bg-slate-700 shadow-sm"
title="Contact Us"
>
Contact
</a>
<a
href="https://cloud.nx.app?utm_source=nx-dev&utm_medium=header"
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center justify-center px-2.5 py-1.5 text-sm font-medium rounded-md transition no-underline bg-blue-500 dark:bg-sky-500 text-white hover:bg-blue-600 dark:hover:bg-sky-600 shadow-sm"
title="Login to Nx Cloud"
>
Login
</a>
</div>
<!-- Social Icons - Hide on screens smaller than 2xl (1536px) -->
<div class="hidden 2xl:flex items-center gap-4">
<div class="flex items-center gap-4">
<SocialIcons />
</div>
<div class="after:content-[''] after:h-8 after:border-l after:border-slate-200 dark:after:border-slate-700"></div>
<!-- Theme Switcher - Always visible (highest priority) -->
<ThemeSelect />
</div>
</div>
@@ -15,27 +15,6 @@ const { hasSidebar } = Astro.locals.starlightRoute;
<div id="starlight__sidebar" class="sidebar-pane">
<div class="sidebar-content sl-flex">
<slot name="sidebar" />
<!-- CTA Buttons for Mobile Menu - Show when not in header (below xl breakpoint) -->
<div class="mobile-cta-buttons xl:hidden mt-auto pt-4 pb-4 border-t border-slate-800 dark:border-slate-700">
<div class="flex flex-col gap-2">
<a
href="https://nx.dev/contact"
class="w-full inline-flex items-center justify-center px-4 py-2 text-sm font-medium rounded-md transition no-underline border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-200 hover:bg-slate-50 dark:hover:bg-slate-700 shadow-sm"
title="Contact Us"
>
Contact
</a>
<a
href="https://cloud.nx.app?utm_source=nx-dev&utm_medium=header"
target="_blank"
rel="noopener noreferrer"
class="w-full inline-flex items-center justify-center px-4 py-2 text-sm font-medium rounded-md transition no-underline bg-blue-500 dark:bg-sky-500 text-white hover:bg-blue-600 dark:hover:bg-sky-600 shadow-sm"
title="Login to Nx Cloud"
>
Login
</a>
</div>
</div>
</div>
</div>
</nav>
+22 -32
View File
@@ -1,43 +1,33 @@
---
import MobileMenuFooter from '@astrojs/starlight/components/MobileMenuFooter.astro'
import SidebarPersister from '@astrojs/starlight/components/SidebarPersister.astro'
import SidebarSublist from './SidebarSublist.astro'
const { sidebar } = Astro.locals.starlightRoute
import Default from '@astrojs/starlight/components/Sidebar.astro';
---
<div class="sidebar-wrapper" data-testid="sidebar-wrapper">
<SidebarPersister>
<SidebarSublist sublist={sidebar}/>
</SidebarPersister>
<div class="md:sl-hidden">
<MobileMenuFooter/>
</div>
<Default {...Astro.props} />
</div>
<style>
.sidebar-wrapper :global(a) {
color: var(--sl-color-gray-4);
}
.sidebar-wrapper :global(a) {
color: var(--sl-color-gray-4);
}
.sidebar-wrapper :global(a[aria-current=page]) {
background-color: transparent;
color: var(--sl-color-text-accent);
font-weight: var(--font-weight-semibold);
}
.sidebar-wrapper :global(a[aria-current=page]) {
background-color: transparent;
color: var(--sl-color-text-accent);
font-weight: var(--font-weight-semibold);
}
.sidebar-wrapper :global(ul ul .large) {
color: var(--sl-color-gray-3);
font-size: var(--text-sm);
font-weight: var(--font-weight-medium);
}
.sidebar-wrapper :global(ul ul .large) {
color: var(--sl-color-gray-3);
font-size: var(--text-sm);
font-weight: var(--font-weight-medium);
}
.sidebar-wrapper :global(details summary .group-label) {
font-size: var(--text-lg);
font-weight: var(--font-weight-semibold);
}
.sidebar-wrapper :global(details summary .group-label) {
font-size: var(--text-lg);
font-weight: var(--font-weight-semibold);
}
.sidebar-wrapper :global(details[open] summary .group-label) {
color: var(--sl-color-gray-2);
}
.sidebar-wrapper :global(details[open] summary .group-label) {
color: var(--sl-color-gray-2);
}
</style>
@@ -1,204 +0,0 @@
---
/*
* This is a modified version of the SidebarSublist component from Starlight with support for icons.
* https://github.com/withastro/starlight/blob/46524ac/packages/starlight/components/SidebarSublist.astro -->
*/
import { Badge, Icon } from '@astrojs/starlight/components'
export interface SidebarLink {
type: 'link'
label: string
href: string
isCurrent: boolean
badge: any
attrs: any
}
export interface SidebarGroup {
type: 'group'
label: string
entries: (SidebarLink | SidebarGroup)[]
collapsed: boolean
badge: any
attrs?: any
}
export type SidebarEntry = SidebarLink | SidebarGroup
interface Props {
sublist: SidebarEntry[]
nested?: boolean
}
const { sublist, nested } = Astro.props
// Copied from https://github.com/withastro/starlight/blob/46524ac/packages/starlight/utils/navigation.ts#L447
function flattenSidebar(entries: SidebarEntry[]): SidebarEntry[] {
return entries.reduce<SidebarEntry[]>((acc, entry) => {
if (entry.type === 'group') acc.push(...flattenSidebar(entry.entries))
else acc.push(entry)
return acc
}, [])
}
function getIconPath(iconName: string | undefined): string | null {
if (!iconName) return null;
return `/docs/images/icons/${iconName}.svg`;
}
---
<ul class:list={{ 'top-level': !nested }}>
{
sublist.map((entry) => {
const icon = getIconPath(entry.attrs?.['data-icon']);
return (
<li class={icon ? 'p-0 border-none' : ''}>
{entry.type === 'link' ? (
<a
href={entry.href}
aria-current={entry.isCurrent && 'page'}
class:list={[{ large: !nested }, entry.attrs?.class]}
{...entry.attrs}
>
{icon && (
<img
aria-hidden="true"
src={icon}
alt=""
class="sidebar-icon w-4 h-4 dark:invert"
/>
)}
<span>{entry.label}</span>
{entry.badge &&
<Badge variant={entry.badge.variant} class={entry.badge.class}
text={entry.badge.text}/>}
</a>
) : (
<details
open={flattenSidebar(entry.entries).some((i: any) => i.isCurrent) || !entry.collapsed}>
<summary class={icon ? 'pl-0 py-2' : ''}>
<div class="group-label">
{icon && (
<img
aria-hidden="true"
src={icon}
alt=""
class="sidebar-icon w-4 h-4 dark:invert"
/>
)}
<span class="large">{entry.label}</span>
{entry.badge && (
<Badge variant={entry.badge.variant} class={entry.badge.class} text={entry.badge.text}/>
)}
</div>
<Icon name="right-caret" class="caret" size="1.25rem"/>
</summary>
<Astro.self sublist={entry.entries} nested/>
</details>
)}
</li>
);
})
}
</ul>
<!-- Copied from https://github.com/withastro/starlight/blob/46524ac/packages/starlight/components/SidebarSublist.astro -->
<style>
@layer starlight.core {
ul {
--sl-sidebar-item-padding-inline: 0.5rem;
list-style: none;
padding: 0;
}
li {
overflow-wrap: anywhere;
}
ul ul li {
margin-inline-start: var(--sl-sidebar-item-padding-inline);
border-inline-start: 1px solid var(--sl-color-hairline-light);
padding-inline-start: var(--sl-sidebar-item-padding-inline);
}
.large {
font-size: var(--sl-text-lg);
font-weight: 600;
color: var(--sl-color-white);
}
.top-level > li + li {
margin-top: 0.75rem;
}
summary {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.2em var(--sl-sidebar-item-padding-inline);
line-height: 1.4;
cursor: pointer;
user-select: none;
}
summary::marker,
summary::-webkit-details-marker {
display: none;
}
.caret {
transition: transform 0.2s ease-in-out;
flex-shrink: 0;
}
:global([dir='rtl']) .caret {
transform: rotateZ(180deg);
}
[open] > summary .caret {
transform: rotateZ(90deg);
}
a {
display: block;
border-radius: 0.25rem;
text-decoration: none;
color: var(--sl-color-gray-2);
padding: 0.3em var(--sl-sidebar-item-padding-inline);
line-height: 1.4;
}
a:hover,
a:focus {
color: var(--sl-color-white);
}
[aria-current='page'],
[aria-current='page']:hover,
[aria-current='page']:focus {
font-weight: 600;
color: var(--sl-color-text-invert);
background-color: var(--sl-color-text-accent);
}
a > *:not(:last-child),
.group-label > *:not(:last-child) {
margin-inline-end: 0.25em;
}
@media (min-width: 50rem) {
.top-level > li + li {
margin-top: 0.5rem;
}
.large {
font-size: var(--sl-text-base);
}
a {
font-size: var(--sl-text-sm);
}
}
}
</style>
+47 -57
View File
@@ -6,13 +6,6 @@ import { PluginLoader } from './plugins/plugin.loader';
import { NxReferencePackagesLoader } from './plugins/nx-reference-packages.loader';
import { CommunityPluginsLoader } from './plugins/community-plugins.loader';
const baseSchema = z.object({
title: z.string(),
/**
* Slug should be from the root route without any prefix requirements i.e. `/docs`
**/
slug: z.string(),
});
// Default docs collection handled by Starlight
const docs = defineCollection({
loader: docsLoader(),
@@ -21,64 +14,61 @@ const docs = defineCollection({
const nxReferencePackages = defineCollection({
loader: NxReferencePackagesLoader(),
schema: baseSchema.and(
z.object({
packageType: z.enum([
'cnw',
'devkit',
'nx-cli',
'nx',
'plugin',
'web',
'workspace',
]),
docType: z.string(), // 'overview', 'generators', 'executors', 'cli', 'migrations', 'devkit', 'ngcli_adapter', etc.
description: z.string().optional(),
category: z.string().optional(),
kind: z.string().optional(),
features: z.array(z.string()).optional(),
totalDocs: z.number().optional(),
npmDownloads: z.number().optional(),
githubStars: z.number().optional(),
lastPublishedDate: z.date().optional(),
lastFetched: z.date().optional(),
})
),
schema: z.object({
title: z.string(),
packageType: z.enum([
'cnw',
'devkit',
'nx-cli',
'nx',
'plugin',
'web',
'workspace',
]),
docType: z.string(), // 'overview', 'generators', 'executors', 'cli', 'migrations', 'devkit', 'ngcli_adapter', etc.
description: z.string().optional(),
category: z.string().optional(),
kind: z.string().optional(),
features: z.array(z.string()).optional(),
totalDocs: z.number().optional(),
npmDownloads: z.number().optional(),
githubStars: z.number().optional(),
lastPublishedDate: z.date().optional(),
lastFetched: z.date().optional(),
}),
});
const pluginDocs = defineCollection({
loader: PluginLoader(),
schema: baseSchema.and(
z.object({
pluginName: z.string(),
packageName: z.string(),
docType: z.enum(['generators', 'executors', 'migrations', 'overview']),
technologyCategory: z.string(),
features: z.array(z.string()).optional(),
totalDocs: z.number().optional(),
description: z.string(),
npmDownloads: z.number().optional(),
githubStars: z.number().optional(),
lastPublishedDate: z.date().optional(),
lastFetched: z.date().optional(),
})
),
schema: z.object({
title: z.string(),
pluginName: z.string(),
packageName: z.string(),
docType: z.enum(['generators', 'executors', 'migrations', 'overview']),
technologyCategory: z.string(),
slug: z.string(),
features: z.array(z.string()).optional(),
totalDocs: z.number().optional(),
description: z.string(),
npmDownloads: z.number().optional(),
githubStars: z.number().optional(),
lastPublishedDate: z.date().optional(),
lastFetched: z.date().optional(),
}),
});
const communityPlugins = defineCollection({
loader: CommunityPluginsLoader(),
schema: baseSchema.and(
z.object({
// community plugins don't have title currently; derive from slug
description: z.string(),
url: z.string(),
lastPublishedDate: z.date().optional(),
npmDownloads: z.number().optional(),
githubStars: z.number().optional(),
nxVersion: z.string().optional(),
lastFetched: z.date().optional(),
})
),
schema: z.object({
slug: z.string(),
description: z.string(),
url: z.string(),
lastPublishedDate: z.date().optional(),
npmDownloads: z.number().optional(),
githubStars: z.number().optional(),
nxVersion: z.string().optional(),
lastFetched: z.date().optional(),
}),
});
// general notification collection for showing time based notifications
@@ -23,9 +23,9 @@ This tutorial requires a [GitHub account](https://github.com) to demonstrate the
### Step 1: Creating a new Nx Angular workspace
Let's [create your workspace](https://cloud.nx.app/create-nx-workspace/angular/github) with our Angular preset to get started quickly.
Let's create your workspace. The setup process takes about 2 minutes and will configure Angular, testing, and CI/CD automatically.
{% call_to_action variant="default" title="Create Angular Workspace" url="https://cloud.nx.app/create-nx-workspace/angular/github" /%}
{% call_to_action variant="default" title="Create Angular Workspace in 2 Minutes ⚡" url="https://cloud.nx.app/create-nx-workspace/angular/github" description="Skip the setup hassle - Get coding instantly with pre-configured CI/CD" /%}
### Step 2: Verify Your Setup
@@ -126,9 +126,9 @@ Finally, commit and push all the changes to GitHub and proceed with finishing yo
> Nx Cloud provides self-healing CI, remote caching and many other features. [Learn more about Nx Cloud features](/docs/features/ci-features).
Click the link printing in your terminal, or you can [finish setup in Nx Cloud](https://cloud.nx.app/setup/connect-workspace/github/select)
Click the link printing in your terminal, or you can connect your existing workspace to Nx Cloud:
{% call_to_action variant="simple" title="Finish Nx Cloud Setup" url="https://cloud.nx.app/setup/connect-workspace/github/select" /%}
{% call_to_action variant="gradient-alt" title="Finish Nx Cloud Setup ☁️" url="https://cloud.nx.app/setup/connect-workspace/github/select" description="Nx Cloud needs to be setup to complete the tutorial" /%}
### Verify your setup
@@ -23,9 +23,9 @@ This tutorial requires a [GitHub account](https://github.com) to demonstrate the
### Step 1: Creating a new Nx React workspace
Let's [create your workspace](https://cloud.nx.app/create-nx-workspace/react/github) with our React preset to get started quickly.
Let's create your workspace. The setup process takes about 2 minutes and will configure React, testing, and CI/CD automatically.
{% call_to_action variant="simple" title="Create Workspace" url="https://cloud.nx.app/create-nx-workspace/react/github" /%}
{% call_to_action variant="inverted" title="Start Building React Apps 10x Faster →" url="https://cloud.nx.app/create-nx-workspace/react/github" description="Zero-config setup with caching, testing, and CI ready out of the box" /%}
### Step 2: Verify Your Setup
@@ -21,11 +21,11 @@ What you'll learn:
This tutorial requires a [GitHub account](https://github.com) to demonstrate the full value of **Nx** - including task running, caching, and CI integration.
{% /aside %}
### Step 1: Creating a new Nx TypeScript workspace (required)
### Step 1: Creating a new Nx TypeScript workspace
Let's [create your workspace](https://cloud.nx.app/create-nx-workspace/typescript/github) with our TypeScript preset to get started quickly.
Let's create your workspace. The setup process takes about 2 minutes and will configure TypeScript, testing, and CI/CD automatically.
{% call_to_action variant="simple" title="Create TypeScript Workspace" url="https://cloud.nx.app/create-nx-workspace/typescript/github" /%}
{% call_to_action variant="gradient" title="Join 1M+ Developers Using Nx Cloud 🚀" url="https://cloud.nx.app/create-nx-workspace/typescript/github" description="Transform your TypeScript workflow - Setup takes less than 2 minutes" /%}
### Step 2: Verify Your Setup
@@ -1,105 +0,0 @@
---
title: 'Optimize Your Time to Green (TTG)'
description: 'Learn how to reduce your Time to Green (TTG) and improve developer productivity'
---
Time to Green (TTG) is the **time from when a pull request (PR) opens and triggers CI to the moment all checks are green and the PR is review-ready**.
TTG is a practical sub-metric of Time to Merge (TTM): by compressing TTG (lower is better), you remove the biggest daytoday bottlenecks that developers feel, which in turn improves overall TTM.
## Why is this important?
<!-- {% ci-bottleneck /%} -->
The biggest daytoday waste in engineering teams: **constant context switching and PR babysitting**. The common loop is:
- 🧑‍💻 Write code
- 🧑‍💻 Push PR
- ⏳ CI runs
- ❌ CI fails 2 minutes later
- ⏳ Discover it much later
- ‍🧑‍💻 Switch context to debug and trigger CI run
- ⏳ Re-running CI
- ❌ Flaky test fails CI
- ‍🧑‍💻 Switch context to debug and trigger CI run
- ⏳ Re-running CI
- ✅ CI is finally green
- ‍🧑‍💻 Reach out to someone to review
This delay compounds across teams and drastically slows delivery. **Nx Cloud fixes this.**
## How to improve TTG
High TTG usually comes from three sources: slow failure discovery, disruptive PR babysitting, and raw execution time. Tackle them in this order.
**Prerequisite: Connect your workspace to Nx Cloud**
If you haven't already, run the following command to connect your workspace to Nx Cloud:
```shell
npx nx@latest connect
```
### 1) Get failure feedback immediately (avoid late discovery)
When you don't notice CI failed, you lose time before you can act. Tighten the loop so failures surface where you're working.
**What to do:** See failures immediately where you work by getting a notification in your editor: install [Nx Console](/getting-started/editor-setup).
### 2) Eliminate PR babysitting (minimize context switching)
The expensive loop is switching branches to fix, repushing, waiting, and repeating; especially with flakes.
**What to do:**
- Approve fixes instead of branchhopping: enable **[SelfHealing CI](/ci/features/self-healing-ci#configure-your-ci-pipeline)** to analyze failed tasks, propose and verify fixes, and commit to your PR after approval.
- Also enable **[flaky task detection and retries](/ci/features/flaky-tasks)** to automatically re-run flaky tasks in the background while you keep working undisturbed.
### 3) Shorten actual CI time (make the pipeline fast)
Once feedback and context switching are handled, compress the compute side.
**What to do:**
- Reuse work with **[Remote caching (Nx Replay)](/ci/features/remote-cache)**.
- Run more in parallel with **[Distributed task execution (Nx Agents)](/ci/features/distribute-task-execution)**.
- Scale long suites with **[E2E test splitting](/ci/features/split-e2e-tasks)** so they finish quickly.
## Measure TTG and diagnose bottlenecks
![TTG metrics](/nx-cloud/recipes/nx-cloud-ttg-stats.avif)
**Doing too much work on PRs**
- Use [Nx Affected](/ci/features/affected) to only run what changed.
**Cache hit rate is low**
- Ensure tasks are cacheable and deterministic. Define `outputs` and configure `inputs`/`namedInputs` correctly. See: [Configure Inputs](/recipes/running-tasks/configure-inputs), [Configure Outputs](/recipes/running-tasks/configure-outputs), [Inputs Reference](/reference/inputs)
- Standardize Node/PNPM versions across dev and CI; avoid environment variables that unintentionally affect inputs.
- Use [Remote caching (Nx Replay)](/ci/features/remote-cache) to share results between CI and dev machines.
**Agents are idle, or queue time is high**
- Increase or right-size distributed capacity and parallelism with [Nx Agents](/ci/features/distribute-task-execution).
- Use [Dynamic Agents](/ci/features/dynamic-agents) to scale based on PR size.
- Remove unnecessary serialization (global locks, [overly strict `dependsOn`](/recipes/running-tasks/defining-task-pipeline)).
**E2E suites take too long**
- Enable [E2E test splitting](/ci/features/split-e2e-tasks) so large suites run across agents.
- Ensure tests are shardable (no hidden global state, independent specs).
**Flaky task rate is high**
- Enable [Flaky task detection and automatic retries](/ci/features/flaky-tasks).
- Isolate and quarantine persistently flaky suites to keep pipelines green.
**Late failure discovery / PR babysitting**
- Install [Nx Console](/getting-started/editor-setup) for instant failure and fix notifications in your editor.
- Enable [SelfHealing CI](/ci/features/self-healing-ci) to propose and validate fixes automatically (ensure the `npx nx-cloud fix-ci` step runs with `if: always()`).
## Talk to us
If you still need help feel free to [reach out to us](/contact).
@@ -1,165 +0,0 @@
---
title: 'Configuring the Cloud Runner / Nx CLI'
description: 'Configure Nx Cloud runner settings in nx.json'
---
The Nx Cloud runner is configured in `nx.json`.
{% tabs %}
{% tabitem label="Nx >= 19.7" %}
```json
// nx.json
{
"nxCloudId": "SOMEID"
}
```
{% /tabitem %}
{% tabitem label="Nx <= 19.6" %}
```json
// nx.json
"tasksRunnerOptions": {
"default": {
"runner": "nx-cloud",
"options": {
"nxCloudId": "SOMEID"
}
}
}
```
To utilize personal access tokens and Nx Cloud ID with Nx <= 19.6, the nx-cloud npm package is also required to be installed in your workspaces `package.json`.
```json
// package.json
{
"devDependencies": {
"nx-cloud": "latest"
}
}
```
{% /tabitem %}
{% /tabs %}
## CI Access Tokens
CI Access Tokens are used in CI environments to provide read-write privileges for pipelines. They should not be committed to source control and should instead be exposed as CI environment secrets.
You can configure CI Access Tokens as environment variables (`NX_CLOUD_AUTH_TOKEN` and `NX_CLOUD_ACCESS_TOKEN` are aliases of each other) or define them in `nx.json` as follows:
{% tabs %}
{% tabitem label="Nx >= 17" %}
```json
{
"nxCloudAccessToken": "SOMETOKEN"
}
```
{% /tabitem %}
{% tabitem label="Nx < 17" %}
```json
"tasksRunnerOptions": {
"default": {
"runner": "nx-cloud",
"options": {
"accessToken": "SOMETOKEN"
}
}
}
```
{% /tabitem %}
{% /tabs %}
## Cacheable Operations
Targets can be marked as cacheable either in the `targetDefaults` in `nx.json` or in the project configuration by setting `"cache": true`. With this option enabled they can be cached and distributed using Nx Cloud.
## Timeouts
By default, Nx Cloud requests will time out after 10 seconds. `NX_CLOUD_NO_TIMEOUTS` disables the timeout.
```shell
NX_CLOUD_NO_TIMEOUTS=true nx run-many -t build
```
## Logging
Setting `NX_VERBOSE_LOGGING=true` when running a command will emit a large amount of metadata It will print information about what artifacts are being downloaded and uploaded, as well as information about the hashes of every computation.
This can be useful for debugging unexpected cache misses, and issues with on-prem setups.
`NX_VERBOSE_LOGGING=true` will also print detailed information about distributed task execution, such as what commands were sent where, etc.
`NX_VERBOSE_LOGGING` is often enabled in CI globally while debugging your CI setups.
## Enabling End-to-End Encryption
All communication with Nx Cloud's API and cache is completed over HTTPS, but you can optionally enable e2e encryption by providing a secret key through `nx.json` or the `NX_CLOUD_ENCRYPTION_KEY` environment variable.
{% tabs %}
{% tabitem label="Nx >= 17" %}
In `nx.json`, add the `nxCloudEncryptionKey` property. It will look something like this:
```json
{
"nxCloudEncryptionKey": "cheddar"
}
```
{% /tabitem %}
{% tabitem label="Nx < 17" %}
In `nx.json`, locate the `taskRunnerOptions` property. Under its "options" property, you can add another property called `encryptionKey`. This is what will be used to encrypt your artifacts. It will look something like this:
```json
{
"tasksRunnerOptions": {
"default": {
"runner": "nx-cloud",
"options": {
"accessToken": "SOMETOKEN",
// Add the following property with your secret key
"encryptionKey": "cheddar"
}
}
}
}
```
{% /tabitem %}
{% /tabs %}
To instead use an environment variable to provide your secret key, run any Nx command as follows:
```shell
NX_CLOUD_ENCRYPTION_KEY=myEncryptionKey nx build my-project
```
This is an alternative to providing the encryption key through `nx.json`, but functionally it is identical.
## Loading Env Variables From a File
If you create an env file called `nx-cloud.env` at the root of the workspace, the Nx Cloud runner is going to load `NX_CLOUD_ENCRYPTION_KEY` and `NX_CLOUD_AUTH_TOKEN` from it. The file is often added to `.gitignore`.
## Disabling Connections to Nx Cloud
If your organization has a security reason to disable Nx Cloud, you can cause all methods of connection to fail by adding the `neverConnectToCloud` property to `nx.json`.
This does not disable the prompts themselves, as the `nx-cloud` package handles this property to provide maximum compatibility with Nx.
A side effect of this is that the `nx-cloud` or `@nrwl/nx-cloud` package may still be installed in your workspace. You can safely remove this, and its presence will send no data (telemetry or otherwise) to Nx Cloud.
You must be on version `16.0.4` or later of `nx-cloud` or `@nrwl/nx-cloud` for this value to be respected.
```json
{
// The following will cause all attempts to connect your workspace to Nx Cloud to fail.
// This value does not prevent using Nx Cloud if already connected.
// Use NX_NO_CLOUD=true env var to prevent using Nx Cloud when running commands
"neverConnectToCloud": true
}
```
@@ -1,918 +0,0 @@
---
title: 'Enterprise Release Notes'
description: 'Release notes for Nx Cloud Enterprise'
---
### 2025.07.1
- Fix: auth redirect loop when using admin login
- Fix: improvement to the flaky task retry mechanism
- Fix: enable run hooks on DTE
### 2025.07
##### Breaking Change
This upgrade includes a breaking change to the `nx-cloud` cluster: instead of a message queue, the `nx-api` pod needs a valid Valkey (Redis) connection string.
1. Install Valkey:
1. You can either use the Bitnami chart: https://github.com/bitnami/charts/tree/main/bitnami/valkey
2. Or for a simpler deployment, you can use the Valkey docker image directly: https://hub.docker.com/r/valkey/valkey/
3. Or you can install it as a system service: https://valkey.io/topics/installation/
2. Upgrade to the latest Helm chart `0.16.3`
3. Apply the following values
```yaml
enableMessageQueue: false
nxApi:
# add these env vars to the nx-api
deployment:
env:
- name: VALKEY_CLIENT_PROVIDER
value: 'redisson'
- name: VALKEY_PASSWORD
valueFrom:
# remember to apply this secret to your cluster
secretKeyRef:
name: valkey-secrets
key: VALKEY_PASSWORD
- name: VALKEY_PORT
value: '6379'
- name: VALKEY_PRIMARY_ADDRESS
value: 'valkey'
- name: VALKEY_USE_SENTINEL
value: 'false'
- name: VALKEY_USERNAME
value: 'default'
- name: NX_CLOUD_CONFORMANCE_RULES_BUCKET
value:
local-cluster-file-server # use this exact value if you are using the file server, otherwise point it to an S3/Azure/Google bucket
# it will use the same role-based auth mechanism you already configured for the NxCloud cache
# you can also use the same bucket name that you use for the cache (rules will just be stored in a sub-folder)
```
##### Updates
- Feat: [Polygraph availability](/ci/recipes/enterprise/polygraph) (Conformance, Workspace Graph, Custom Workflows)
- Feat: Nx 21 [continuous tasks](/blog/nx-21-continuous-tasks) support
- Feat: Download artifacts button
- When you view a task that just ran in CI on the NxCloud UI, there is now a button to download any artifacts that task produced directly from your browser
- This is especially useful if you want to view screenshots/videos of failed e2e tests
- Feat: [Self-healing CI](/ci/features/self-healing-ci)
- Speak to your assigned DPE about testing this
- You will need an Anthropic API key and access to Claude's servers
- Feat: Dark Mode UI setting
- Various fixes and stability improvements to DTE, agent visualization, and other areas of the app
### 2025.06.3
- Fix: add timeouts to GitLab requests
- the defaults are now 5 and 10 seconds for connect and read
- these should help prevent issues with certain unstable GitLab environments
### 2025.06.2
- Fix: Terminal outputs not loading in the browser in restricted environments
- Requires an update to the latest current nx-cloud Helm chart version 0.16.3
### 2025.06.1
- Fix: GitHub connection issue on nx-api startup when Nx Agents are active
### 2025.06
- Feat: Define your own custom resource classes (CPU, RAM etc.) for use with Nx Agents
- See [configuration details](https://github.com/nrwl/nx-cloud-helm/blob/main/EXTERNAL-RESOURCE-CLASSES.md)
- Feat: Flaky task retry configuration
- Configure in the workspace settings how Nx Cloud should handle flaky tasks
- Feat: Full GitLab integration
- Allows automatic members sync to your Nx Cloud workspace
- Feat: Agent logs timestamps and full-screen mode
- Feat: Assignment rules updates
- Parallelism configuration
- Target globs
- See [here](/ci/reference/assignment-rules#how-to-define-an-assignment-rule) for examples
- Feat: Parallel agent steps and step groups (docs [here](/ci/reference/launch-templates#launchtemplatestemplatenamegroupname))
- Feat: Reusable agent launch template snippets via yaml anchors
- See example [here](/ci/reference/launch-templates#full-example)
- Specifically how `common-init-steps: &common-init-steps` is defined
- Feat: Individual GitHub commit statuses for each run group
- This is configurable in your workspace settings
- See [here](/ci/recipes/source-control-integration/github#github-status-checks) for branch settings configuration
- You might also need [to update your GitHub app permissions](/ci/recipes/enterprise/single-tenant/custom-github-app#configure-permissions-for-the-github-app)
- Misc: CIPE list is sortable by duration
- Fix: early DTE job termination improvements
- Fix: run details page performance improvements
### 2025.03.3
- Feat: provide prebuilt Java cert store to NxAPI
- Full details [here](https://github.com/nrwl/nx-cloud-helm/blob/main/PROXY-GUIDE.md#pre-built-java-cacerts)
### 2025.03.2
- Feat: Nx Agents "bundled executors"
- up until now, the "executor" binaries that run on each Nx Agent (and know how to parse your agents.yaml and run each step) had to be downloaded separately from an external bucket
- this made the on-prem upgrade process more difficult, as it required a separate step to download the executor and then upload it in the correct folder on an internally available repository
- now, the executors are available as Docker images that can be imported alongside all your other NxCloud images
- to get started:
- when upgrading to this version, make sure you also pull in the executor image `nxprivatecloud/nx-cloud-workflow-executor:2025.03.2`([link](https://hub.docker.com/repository/docker/nxprivatecloud/nx-cloud-workflow-executor/tags/2025.03.2/sha256-a42835a3126f21178af87f02b68d68fec1ff0654d37a57855a762c01e7795a6b))
- as part of your controller [args](https://github.com/nrwl/nx-cloud-helm/blob/main/charts/nx-agents/values.yaml#L76) pass this option:
```
args:
# pass the internal image registry where the pods can pull the executor images from
# for example: image-registry: us-east1-docker.pkg.dev/nxcloudoperations/nx-cloud-enterprise-public
image-registry=<registry-where-nxcloud-images-are-hosted>
# you can REMOVE the below option, as it's not needed anymore
# kube-unix-init-container-name=...
```
- you no longer need to upload the executor binary separately to a bucket
- now whenever you start your agent pods, they will load the above image and copy the executor from there
### 2025.03.1
- Fix: use custom "github URL" (if defined) when checking out the repo on Nx Agents
### 2025.03
##### Assignment rules
Assignment rules allow you to control which tasks can run on which agents. Save on agent costs by provisioning different sizes of agents to suit the individual needs of your tasks. You can ensure resource intensive targets like `e2e-ci` and `build` have what they need by using larger agents. Lighter tasks like `lint` and `test` can run on smaller agents.
Assignment rules are defined in yaml files within your workspace's `.nx/workflows` directory. You can use assignment rules with DTE-agents or with dynamic Nx Agents. Note that additional configuration is required when using DTE agents.
Read the full docs [here](/ci/reference/assignment-rules#assignment-rules-beta).
Once you start using assignment rules, you'll be able to see all your configured "rules" in your CIPE "Analysis page".
##### DTE/Agent utilization visualization
Speaking about the CIPE "Analysis" page, the agent utilization graph has been completely revamped.
The new agent utilization visualization allows you to see when agents were actively executing tasks, and gaps when agents were idle. You can use this tool to optimize how work gets distributed, and modify your commands and dependencies to remove idle time. Tasks that hang will be highlighted in yellow, helping you debug OOM issues. If youre using Nx Agents, youll also see set up steps on the visualization.
##### Workspace data caching
Before an Nx command is run, Nx will generate some metadata that it will use when evaluating tasks (i.e project graph) and store that data in the workspace-data folder. This short process is relatively quick for small repos and only needs to be performed upon the first call to Nx. However, for larger repos and cases where Nx is frequently generating this information from scratch, it becomes a large time sink. In CI, workspace-data needs to be generated each time a new pipeline is run and each agent needs to generate its own identical copy.
You can now set-up NxCloud to cache the default branch's workspace data and allow pipeline agents to retrieve it from the cache rather than regenerate this metadata each time.
To enable it, you need to set this env variable on the nx-api deployment:
- `NX_CLOUD_WORKSPACE_ARTIFACTS_STORAGE_BUCKET=<cloud-provider-bucket-name>`
- it will then use the same authentication mechanism you've set up for your main cache bucket
- if you do not use a cloud provider bucket such as S3, you can set this variable to `NX_CLOUD_WORKSPACE_ARTIFACTS_STORAGE_BUCKET=file-server` and it will use your local cluster file server
##### Misc Items
- a new version of the AMQ image was released with the latest security patches and fixes
- node modules caching fixes on Nx Agents
- previously, we were always recommending caching the `node_modules` folder itself in your Nx Agents yaml configs
- this does not work with `npm ci`, as it always deletes the local `node_modules` folder before starting the installation. Instead, NPM recommends caching the `$HOME/.npm` directory.
- Yarn and PNPM also have their own dedicated folders they recommend for caching
- Part of this release, we now fixed caching folders in the `$HOME` directory, so all the below options should work:
- `~/.npm`
- `~/.cache/yarn`
- `.pnpm-store` (note PNPM on Nx Agents does not store its cache folder in the $HOME dir)
- Please refer to the [custom launch templates docs](/ci/reference/launch-templates#full-example) for how you can setup your caching under these new recommendations
- Nx Agents `$HOME` directory mounting
- previously, when your Nx Agents pods were starting up, we were mounting as a k8s volume just the folder in which you checkout your repo: `$HOME/workspace`
- however, a lot of dependencies and third-party apps use `$HOME` folder to deposit a lot of files (Rust, NPM cache folders etc.)
- this caused agents to fight for available space on the node itself, causing very hard to debug issues if the space requirements were too big
- part of this release, we now mount the whole `$HOME` directory as a volume, ensuring each agent gets a predictable storage size allocated
- this also enables Nx Agents to run in more restricted environments (such as OpenShift), where read-only file systems are enforced (due to mountable volumes being writeable)
- to enable this:
- ensure you use (or are extending from) one of our pre-built agents base images
- this is the image you set in your `image:` portion of your `agents.yaml`
- (you are most likely using one of our images, so you can probably skip this step)
- if you had to import the above image into your own internal registry, ensure it is part of a repository called `nx-agents-base-images`
- Example (see the `nx-agents-base-images` part in this path): `image: 'us-east1-docker.pkg.dev/nxcloudoperations/nx-cloud-enterprise-public/nx-agents-base-images:ubuntu22.04-node20.11-v12'`
- enable the `--copy-home-dir-init-container` flag [on the `controller.deployment.args` section in your Nx Agents `helm-config.yaml`](https://github.com/nrwl/nx-cloud-helm/blob/main/charts/nx-agents/values.yaml#L69)
- increase restart amount for agents
- if any of your agents go down (either because one of their init steps fails, due to networking issues for example) or they run out of memory, we now try to restart them up to `N` times, where `N` is the number of agents you have
- this should result in more pipeline stability, though it is worth to still monitor the failed steps to ensure any persistent issues get addressed
- addresses various potential race conditions in the NxCloud runner when restoring items from the cache (this was mainly noticeable on very large workspaces)
- various UI issues with the "compare tasks diff" have now been addressed
- this is the tool used to diagnose why a cache hit did not occur and what the differences are between two given hashes
### 2025.01.4
- Misc: adds new custom Nx Agents resource classes
### 2025.01.3
- Misc: adds new custom Nx Agents resource classes
### 2025.01.2
- Fix: issue with decoding certain branch names in the URL (fixes loading certain run pages)
### 2025.01.1
- Fix: adds data migrations for older organizations
### 2025.01
##### Affected project graph
The affected project graph for pull requests has now made it to the on-prem release! Read the full announcement [here](/blog/ci-affected-graph).
##### DTE improvements
- There have been a lot of performance improvements to the DTE algorithm and how tasks get sorted to ensure optimal distribution
- Improved early agent shutdown: we now look at more parameters to decide whether we can shutdown a DTE agent earlier
- Project graph integrity checks
- both the main job and the agents require the exact same project graph for the DTE algorithm to run correctly
- differences can appear, for example, if the agents or main job restore an older cached version of the project graph (instead of re-calculating the current one)
- it can also happen if the main job and agents run off of different commits (maybe your main CI job does a `git merge` with `main` and your agents do not)
- we now explicitly check if the agents and main job run on the same exact commit hash and also if they use the same project graph: otherwise we fail the DTE early
- `stop-agents-after` now supports target configs
- if you are running two affected commands at different points in your main job, each triggering the same target but under different configurations
- `nx affected -t build:config1`
- `nx affected -t build:config2`
- you can configure agents to wait for both of them to complete before ending the DTE
- `--stop-agents-after=build:config1,build:config2,lint,test`
##### Nx Agents improvements
Previously, if an agent ran out of memory or crashed in the middle of its run, the logs would be lost.
Now, we have a dedicated long-running "log uploader" that can upload logs even if the main agent container crashed.
To enable, you will need to configure the following env var on your workflow controller:
```yaml
- name: LOG_UPLOADER_IMAGE
value: 'us-east1-docker.pkg.dev/nxcloudoperations/nx-cloud/nx-cloud-workflow-log-uploader'
```
We now also make much fewer requests to GitHub (or your other VCS providers) during a CIPE start, so you should see improved Nx Agents startup times.
##### PR comments look refresh
The PR comment containing status updates about your CI execution has had make-over, showing a more clear breakdown of your runs, their duration and the status:
![new PR comment](/nx-cloud/reference/images/new_github_comment.png)
##### Misc Items
- while we do our best to infer your commit message to display on the CIPE page, if it ever doesn't look right, you can manually override in your CI pipeline by setting `NX_CLOUD_COMMIT_MESSAGE`
- improved workspace analytics controls
- we now print more information on the main CI job summary table, such as a direct link to the associated CIPE
- there is now a workspace level setting enabling or disabling flaky task retrying
### 2024.10.3
- Feat: Support NO_PROXY env var on pods
### 2024.10.2
- Fix: AWS S3 bucket connections when using STS role-based authentication
### 2024.10.1
- Fix: GitHub and external bucket connection issues when using a proxy
### 2024.10
This is a big release so let's go through the highlights first. There is also an important "Breaking changes" section at the end.
##### New Version Structure
We have changed our version structure to a more simplified tag: `YEAR.MM.PATCH_NUMBER`
The goal is to make it easier to spot how old/recent your existing NxCloud version is and compare it against newer deployments.
##### DTE Summary Table on Main Agent
When distributing with DTE, up until now, we have been replaying all your tasks logs "as they come in" from the DTE agents back onto your main job.
This is not that useful on big workspaces, with large task affected task graphs as it can be hard to follow all the outputs from all the agents streaming back concurrently.
This release contains the new "CI Table Log View" summary, and you can read all about it [here](/blog/improved-ci-log-with-nx-cloud).
If you prefer the old logs style, you can always disable the feature via your workspace's settings screen.
##### Personal Access Tokens
Up until now, for developers to get access to read (and maybe write) to the cache you always needed an access token to be made available locally: either
committed to the repo via `nx.json` or made available as an env variable via a `.local.env` file.
This flow was secure enough as is, even if you never rotated your access tokens, as someone would still need continuous hourly access to your source code if they wanted to retrieve any of the latest cached artifacts.
But given you already manage developer access to your NxCloud workspace via the web app, we wanted to tie local cache access to that mechanism as well.
This release contains the new ["Personal Access Tokens"](/blog/personal-access-tokens) feature that now asks developers to login locally before they can use the cache.
If they are a member of the workspace, they get a local token stored on their machine that will be used to access the cache.
The moment they get removed as a member from your workspace, they won't be able to read from the cache anymore.
Please read the full announcement post here, as it contains details on how to migrate your team to using [Personal Access Tokens](/blog/personal-access-tokens).
##### GitHub App Integration
If you are using GitHub, setting up a custom GitHub app for your org is the best way to take advantage of all the latest "GitHub-specific" features we offer.
Please see instructions [here](/ci/recipes/enterprise/single-tenant/custom-github-app) on setting up an app.
You will then need to make sure you set up your VCS integration again through your workspace's settings screen, and use the above app you created.
As part of this, you will also get the "GitHub membership management" feature, where everybody who is a collaborator of your GitHub repo will also get "read" access to your NxCloud workspace,
without you having to explicitly invite them.
##### Misc Items
- Improved docker agents support
- We fixed a few issues related to running docker builds in Nx Agents
- Big DTE performance improvements
- Azure file storage for Nx Agents
- Auth session length has been extended to 7 days by default
- Use NX_CLOUD_SESSION_MAX_AGE to configure this to a different value
- Various SAML fixes and improvements
- One highlight is that users can now login from Okta directly (while before they had to initiate login through NxCloud web app)
##### Breaking Changes
Most workspaces will not be affected by this, but if you have these values configured in your `helm-config.yaml`:
- `github.pr.[...]`
- or `gitlab.mr.[...]`
they will stop working with this release (see [this](https://github.com/nrwl/nx-cloud-helm/pull/141/files) for details on what was removed).
Please go to your workspace settings and you should be able to configure all the above values when you setup a VCS integration.
_Terminal outputs_ in the web app will now be fully served from storage bucket (either S3/Gcloud/Azure, or your internal file-server). This means your NxCloud cluster needs to have an open/healthy connection to the bucket. You can test this by ssh'ing into the `nx-cloud-frontend` pod and trying to `wget` one of your bucket artefacts. Any proxy or firewall constraints will need to be handled. Additionally, if your bucket is hosted at a self-signed https URL, any fetch calls from the frontend pod to your bucket will fail. If you think any of this applies to you, please contact your DPE to discuss options.
### 2406.29.1.patch1
- Fix an issue with specifying custom AWS credentials in Minio instances
- Fix an issue with removing pending invites
### 2406.29.1
##### Full terminal outputs in the web app
Due to storage constraints in Mongo, long terminal outputs were sometimes truncated when viewed in the UI. With this update we are now loading all terminal outputs directly
from the storage bucket, removing the need to keep them in Mongo. You should now be able to view full, complete logs in the UI regardless of how large the output is.
##### OpenShift fixes for Agents
- the latest messagequeue image is now OpenShift ready
- to use, just update to the latest Helm version `0.15.6` and make sure you are not passing in an explicit tag for the messagequeue
- then use version `2406.29.1` for NxCloud. This should use the latest, OpenShift enabled messagequeue image
- when running Agents on OpenShift, they run as a specific user with ID 1000
- to override this, make sure to set `NX_CLOUD_RUN_UNIX_PODS_AS_USER: <userId>` and `NX_CLOUD_RUN_UNIX_PODS_AS_GROUP: <groupId>` on the [workflow controller env vars](https://github.com/nrwl/nx-cloud-helm/blob/main/charts/nx-agents/values.yaml#L63)
##### Full Bitbucket Data Center (on-prem)
We now have full support for BitBucket Data Center (self-hosted):
- VCS integration for posting comments with live updates about your CI runs
- full agents integration
- more info about each one of your commits on the NxCloud web app
- you can even [set-up auth with BitBucket Data Center](/ci/recipes/enterprise/single-tenant/auth-bitbucket-data-center#bitbucket-data-center-auth)
##### Misc
- easier workspace setup experience for new customers
- the CIPE visualisation has been updated (elapsed task time)
- general web app performance improvements
##### Breaking changes
If you are using DTE, you will now need to pass the `--distribute-on="manual"` flag to your `npx nx-cloud start-ci-run` commands.
### 2405.02.15
##### Easy membership management via GitHub
A few months ago, we introduced a new feature to our managed SASS NxCloud product: easy membership management via GitHub. If you create a new workspace on [https://cloud.nx.app/](https://cloud.nx.app/) right now you will be guided through how to connect it to your GitHub repository. Now everyone that has access to your GitHub repository will also get access to your NxCloud workspace. If anyone loses access to GitHub (maybe they leave the company), they will also lose access to NxCloud. This makes membership management easy and straightforward, as you don't have to manually invite users anymore. Of course, setting up this connection also gives you NxCloud run status updates directly on your PRs - a feature we've had for a long time.
This feature has now been release for on-prem set-ups as well. To benefit from it, you'll need to create your own Github App with permissions to access your repository. Your on-prem NxCloud instance will then use this app to pull membership info from Github and check user permissions. You can find [the full setup instructions here](/ci/recipes/enterprise/single-tenant/custom-github-app).
##### DTE v2 enabled by default
After testing the improved task distribution algorithm (DTE v2) for the past few months, we are now enabling it by default for all customers. Expect quicker CI run times when using DTE, and better utilization of your agents with less idle time.
##### Nx Agents and breaking changes
If you are using Nx Agents, this release will contain a breaking change to the workflow controller.
Before upgrading to this version, you'll need to follow the new [Agents Guide](https://github.com/nrwl/nx-cloud-helm/blob/main/agents-guide/AGENTS-GUIDE.md) and deploy an instance of Valkey that your controller can connect to.
The reason we need Valkey is that the workflow controller now persistently stores information about your workflows for up to 8 hours, and these changes will be persisted regardless of the availability of the workflow controller pod, making your in-progress workflows more resilient to rolling kubernetes updates, and will fix some previous issues with agent statuses not syncing to the UI.
If you are not using Nx Agents, this does not affect you and you can upgrade to this version straight away.
##### UI improvements
- If you are using the new Crystal plugins in Nx 18, we've now added a "technologies label" to each task, so you can quickly see which tasks are Playwright based, Cypress, React etc.
- We've added toast notifications in the app. You'll see them confirming some of your actions, such as saving workspace changes.
##### Misc fixes
- We've fixed various bugs around the task distribution algorithm and Nx Agents. CIPEs using distribution should feel more stable and faster.
- We fixed a few issues relating to the GitLab and BitBucket integrations.
### 2404.05.9
##### DTE Algorithm V2 Experimental Flag
For the past 2 months, we've been re-writing our entire task distribution algorithm. The aim was to allocate tasks more efficiently to agents,
reduce total time spent by agents downloading artefacts and reduce idle agent time waiting for tasks.
While the features is still in its beta stage, initial tests do show a big improvement in overall CI completion time
(but this varies on a case by case basis).
If you are already using DTE or Nx Agents, you can enable this experimental feature by adding the following env var to your main job (the job
where you invoke `npx start-ci-run`):
```yaml
NX_CLOUD_DTE_V2: 'true'
```
##### Nx Agents On-Prem Availability
Since the previous release, we've been testing various options for deploying Nx Agents on-premise.
We now have a dedicate Helm chart dedicated to setting up an Nx Agents cluster on your infrastructure:
1. ⚠️ Please reach out to your DPE first so we can start an Nx Agents trial and discuss any limitations and requirements up-front
2. You can view the example `values.yaml` [here](https://github.com/nrwl/nx-cloud-helm/blob/main/charts/nx-agents/values.yaml)
3. Once we had a chance to look at your existing CI compute requirements, you deploy the chart via `helm install nx-cloud nx-cloud/nx-agents --values=helm-values.yml`
##### Audit logger
As an Enterprise installation admin, you can now view audit logs over NxCloud workspace events by visiting `https://<NXCLOUD-URL>/audit-log`.
This includes events such as when a workspace was created, when a new VCS integration was set-up and so on.
##### UI improvements
- Organizations can now be created directly from the "Connect a workspace" screen
- Full screen terminal outputs for your tasks
### 2402.27.3.patch3,4,5,6
- Feat: allows customising the base image for the agent init-container (in case it is self-hosted internally in the company)
- Feat: adds more logging to debug authorization errors for Github, Gitlab and SAML
- Fix: fixes an issue with using custom launch templates on GitLab
### 2402.27.3.patch3
- Feat: allows disabling the automated pod watcher which doesn't behave as expected in some k8s engines
### 2402.27.3.patch2
- Feat: allows volume class to be customised for Agents
### 2402.27.3.patch1
- Fixes an issue with the aggregator creating empty organisations during the first migration
### 2402.27.3
With this version you can take advantage of most features announced during our recent [launch week](/launch-nx).
##### Nx Agents
This release contains everything needed to run [Nx Agents](/ci/features/distribute-task-execution) on-prem. While the on-prem configuration is still experimental, we are actively running Nx Agents trials at the moment, and if you'd like to take part please reach out to your DPE.
If you already running DTE, there are a few advantages to upgrading to Agents:
- simplified CI config: you will need to maintain just a single, main CI job config. NxCloud will create needed CI agents for you as needed.
- [dynamic agent allocation based on PR size](/ci/features/dynamic-agents): instead of always launching all your agents NxCloud will now launch different number of agents dynamically based on your PR size
- access to [Spot instances](https://aws.amazon.com/ec2/spot/): if you are running your clusters on any of the popular cloud providers (AWS, Google Cloud, Azure etc.), you can now use their Spot instances for running your CI job. This is possible due to NxCloud's distribution model, which allows work on a reclaimed node to be re-distributed to the remaining agents.
We will shortly make available a new Helm chart that will allow you to deploy a separate Agents cluster to launch workflows: [https://github.com/nrwl/nx-cloud-helm](https://github.com/nrwl/nx-cloud-helm).
![agents_screen](/nx-cloud/reference/images/agents.webp)
##### Task Atomizer and task retries
If you combine this release + upgrade to the latest Nx 18, you will have access to both the [task atomizer](/ci/features/split-e2e-tasks) (which allows your e2e to be distributed among agents PER FILE, instead of previously per project) and the [flaky task retry functionality](/ci/features/flaky-tasks).
##### CIPE page improvements
Along with all the UI changes to support agents (following their logs and track how tasks get distributed,details of which you'll find demoed on [this page](/ci/features/distribute-task-execution)) this release also brings all the new improvements to the CI pipeline execution page, including the commit info panel at the top:
![cipe_top_half_screen](/nx-cloud/reference/images/cipe_top.webp)
### 2312.11.7.patch1
- re-enable path style access for s3 buckets
- fix an aggregator migration issue with old CIPE data
### 2312.11.7
##### Helm package compatibility
When upgrading to this version and anything above it, you will need to use Helm version 0.12.0+:
| Chart Version | Compatible Images |
| :-----------: | :--------------------------------: |
| <= `0.10.11` | `2306.01.2.patch4` **and earlier** |
| >= `0.11.0` | `2308.22.7` **and later** |
| >= `0.12.0` | `2312.11.7` **and later** |
##### New UI features and improvements
On the UI, we replaced the runs overview with the new CI Pipeline Executions (CIPE for short) screen:
![cipe_screen](/nx-cloud/reference/images/cipe_screen.webp)
This screen organises your runs more logically, according to each invocation of your CI pipeline.
It provides more data around the committer name and commit message and a full analysis of your CIPE once it is completed.
And if you need to run your tasks on multiple environments, you can now switch between them on this page and view the results separately.
You can play around with an example on the [Nx Repo](https://staging.nx.app/orgs/62d013d4d26f260059f7765e/workspaces/62d013ea0852fe0a2df74438/overview)
There is also a new Analytics screen for your workspaces, to which we'll keep adding new features to better help you optimise your CI pipelines:
![analytics_screen](/nx-cloud/reference/images/analytics_screen.webp)
Here you can see:
- historical trends of CIPE Average duration
- historical trends of CIPE average daily count
- average daily time saved by DTE
Other improvements:
- better overall UI performance (navigating feel much snappier now)
- improved terminal output rendering
- members can now be invited as admins directly
##### The light runner
Nx Cloud works by using a local Node runner that wraps your Nx tasks and sends information about them to the Nx Cloud API. This is how it knows whether to pull something from the remote cache or run it.
Because they work together, sometimes changes to the API required updates to this local runner. This led to workspaces that did not update their local
runner version in `package.json` sometimes running into compatibility issues.
We overhauled this mechanism, and the runner is now bundled as part of the API itself, ensuring you get sent the correct runner code when you first start running Nx commands in your workspace.
This ensures you will always have the correct local runner version that is compatible with your on-prem Nx Cloud installation.
We've been testing this out on our Public Nx Cloud instance and it is now available for on-prem installations as well.
To enable the light runner feature, make sure you:
1. remove `useLightClient: false` from your `nx.json` (if you had it)
2. If you are on Nx version > 17, you can remove any `nx-cloud` or `@nrwl/nx-cloud` package in your `package.json` and it should just work
3. If you are on Nx version < 17, upgrade to `nx-cloud@16.5.2` or `@nrwl/nx-cloud@16.5.2`.
##### Nx Agents
This release is also the first one to support ["Nx Agents"](/ci/features/distribute-task-execution).
While currently experimental and disabled by default for on-prem users, we are looking for more on-prem workspaces to try it out with
so please reach out to your DPE contact or to [cloud-suppport@nrwl.io](mailto:cloud-support@nrwl.io) if you are interested in helping us shape this according to your needs!
##### Breaking changes - MongoDB migration
As a reminder, we now only support MongoDB 6+. If you are running an older version please refer to the upgrade instructions [here](/ci/reference/release-notes#breaking-changes).
### 2308.22.7.patch7
- Allows the frontend container to be ran with `runAsNonRoot: true`
### 2308.22.7.patch6
- Fixes a UI issue on the branch when running a task in a DTE context
### 2308.22.7.patch5
- Fixes a UI issue when navigating to branches containing slashes
### 2308.22.7.patch4
- Updates the frontend image to remove some vulnerability issues
### 2308.22.7.patch3
- Fixes a compatibility issue with the latest `nx-cloud` release
### 2308.22.7.patch2
- Fix: github member invites
### 2308.22.7.patch1
- Feature: self-signed certificate support for aggregator
- This is needed if you are using self-signed certificate for your external Mongo instance
- See [here](https://github.com/nrwl/nx-cloud-helm/blob/main/PROXY-GUIDE.md#supporting-self-signed-ssl-certificates) for usage details.
- Fix: aggregator issue when creating text Mongo indexes
### 2308.22.7
In our last big release, we announced a completely new UI, rebuilt from the ground up in React. In this release, the frontend team
has continued that effort and wrapped the React app with the [Remix](https://remix.run/) framework. This is the same technology that powers our public https://cloud.nx.app/
product. It's faster, it handles resource caching better, and should allow the frontend team to ship features quicker than ever before.
##### Helm package compatibility
When upgrading to this version and anything above it, you will need to use Helm version 0.11.1:
| Chart Version | Compatible Images |
| :-----------: | :--------------------------------: |
| <= `0.10.11` | `2306.01.2.patch4` **and earlier** |
| >= `0.11.0` | `2308.22.7` **and later** |
##### VCS proxy support
- For the GitHub/Bitbucket/Gitlab integrations to work, Nx Cloud needs to make HTTP calls to GitHub/GitLab to post comments
- If are behind a proxy however, these requests might fail
- If you are using our [Helm chart](https://github.com/nrwl/nx-cloud-helm/), you can now configure this option to unblock the vcs integration and allow it to work with your proxy:
```yaml
vcsHttpsProxy: '<your-proxy-address>'
```
##### Misc updates
- UI enhancements of the run details screen
- UI enhancements of the task details screen
- fixes and better error handling for the DTE screen
- failed runs are now sorted at the top
- web app performance improvements for large workspaces
- more structured NxAPI pod logs (allows for better debugging)
##### Bug fixes
- Fixed an issue with applying licenses on orgs owned by non-installation admin accounts
##### Breaking changes - MongoDB migration
In the last big release we announced [the deprecation of Mongo 4.2](/ci/reference/release-notes#breaking-changes)
With this release, we have now stopped supporting Mongo 4.2 completely. Please upgrade Mongo to version 6 before installing this new image. You will find instructions [here](/ci/reference/release-notes#breaking-changes).
### 2306.01.2.patch4
- Fixes an issue with new licenses expiring sooner than original end date
### 2306.01.2.patch3
- Fixes an issue with multiple admin organizations being created on new installations
- Fixes an issue where Enterprise licenses could not be applied on some new orgs
### 2306.01.2.patch2
- Fixes an issue with the `single-image` container where the aggregation would block the API from starting up
### 2306.01.2.patch1
- Fixes an issue where admin users were not being created on new installations.
### 2306.01.2
This is one of our biggest Nx Cloud On-Prem releases. It also marks a change in our release process which will be explained at the end.
##### Brand new UI
A few months ago we announced a complete re-design of the Nx Cloud UI! It's faster, easier to use and pleasant to look at! We're now bringing this to On-Prem users as well:
You can read more about it in our [announcement blog post](/blog/nx-cloud-3-0-faster-more-efficient-modernized).
##### Pricing updates
While before we provided you with a separate coupon for each workspace, we have now changed to "organization-wide licenses": you receive a single coupon for your whole organization, that gives you unlimited access for the agreed number of workspaces. You are then free to delete, create and re-shuffle your workspaces as often as you want without requiring new coupons for us (as long as you stay within your limit of workspaces).
You will see some updates in the UI to reflect this, however, **you don't need to do anything once you update your images!** We'll automatically migrate you to this, based on your current number of enabled workspaces!
##### Proxy updates
One of the features of Nx Cloud is its integrations with your repository hosting solution. When you open up a Pull Request, you can configure Nx Cloud to post a comment to it once your CI has finished running, with a summary of all the tasks that succeeded and failed on that code change, and a link to your branch on Nx Cloud so you can further analyse your run. Your developers save time, and allows them to skip digging through long CI logs.
Before, if you had a self-hosted instance of GitHub, Gitlab or Bitbucket, calls from Nx Cloud to your code-hosting provider would fail, because they'd be using a self-signed certificate, which Nx Cloud wouldn't recognise.
[We now support self-signed SVN certificates, via a simple k8s configMap.](https://github.com/nrwl/nx-cloud-helm/blob/main/PROXY-GUIDE.md#supporting-self-signed-ssl-certificates)
[We've also made updates to the runner, to support any internal proxies you might have within your intranet.](https://github.com/nrwl/nx-cloud-helm/blob/main/PROXY-GUIDE.md#supporting-self-signed-ssl-certificates)
##### DTE performance
We completely re-wrote our Task Distribution engine, which should result in much fewer errors due to agent timeouts, increased performance and more deterministic task distribution.
We've also added a new internal task queueing system, which should further improve the performance of DTE. While this is an implementation detail which will be automatically enabled in future releases, you can test it out today by setting [`enableMessageQueue: true`](https://github.com/nrwl/nx-cloud-helm/blob/main/charts/nx-cloud/values.yaml#L18) in your Helm config.
You can read more about the recent DTE improvements in our [Nx Cloud 3.0 blog post](/blog/nx-cloud-3-0-faster-more-efficient-modernized).
##### Misc updates
- We have fixed issues related to OpenShift deployments. [Special thanks to minijus](https://github.com/nrwl/nx-cloud-helm/pull/32) for his work on the Helm charts and helping us test the changes.
##### Breaking changes
Nx Cloud uses MongoDB internally as its data store. While we've always used Mongo 4.2, in the latest release we started targeting Mongo 6.0. It's a much lighter process, with improved performance, and quicker reads and writes.
While you can still upgrade to this new image even if you are on Mongo 4.2 (nothing will break), **we strongly recommend you upgrade your Database to Mongo 6.0 to make sure nothing breaks in the future.** [We wrote a full guide on how you can approach the upgrade here](https://github.com/nrwl/nx-cloud-helm/blob/main/MONGO-OPERATOR-GUIDE.md#upgrading-to-mongo-6).
If you need assistance, please get in touch at [cloud-support@nrwl.io](mailto:cloud-support@nrwl.io).
###### Migration from Community Edition to Enterprise
On May 16th, 2023 we announced our plans to sunset the Community Edition of Nx Cloud On-Prem to align with our new pricing plans. If you are on the Community Edition, please follow these steps to migrate:
1. Use this image: `2306.01.2.patch3`
2. Switch to private Enterprise by setting `NX_CLOUD_MODE=private-enterprise` (or `mode: 'private-enterprise'` if using Helm).
3. Reach out to us at [cloud-support@nrwl.io](mailto:cloud-support@nrwl.io). You will get a FREE, unlimited-use coupon for the next 3 months so you can trial Nx Enterprise.
##### New release process
With this update, we are also changing our release process:
1. We'll start adding release notes with every new version published
2. We switch to using [calver](https://calver.org/) versioning for our images
3. We stopped publishing the `latest` tag.
4. We will be emailing Enterprise admins with every new release. If you do not get these emails, please send us an email at [cloud-support@nrwl.io](mailto:cloud-support@nrwl.io) to get added
Any questions at all or to report issues with the new release [please get in touch!](mailto:cloud-support@nrwl.io)
### 13-02-2023T23-45-24
- Feat: Targettable agents for DTE. You can now ask specific agents to pick up specific tasks (via `--targets
- Fix: DTE fixes for 404 not found artefacts errors
- Fix: issue when using GitHub integration with self-hosted GitHub instances
### 26-01-2023T21-22-48
- Misc: Fixes to the Gitlab integration
### 05-01-2023T17-53-45
- Misc: This release contains small bug fixes and UI improvements.
### 14-12-2022T19-43-44
- Feat: IAM Role Auth. We have now deprecated "aws_access_key_id" and "aws_access_key_secret" in favor of service accounts and IAM roles for accessing AWS resources. See the [new guide here](https://github.com/nrwl/nx-cloud-helm/blob/main/aws-guide/AWS-GUIDE.md) for details.
### 13-10-2022T16-45-30
- Misc: This release mostly contains improvements that apply to the Public SASS version of Nx Cloud. No significant changes for the On-Prem version.
### 13-10-2022T16-45-30
- Feat: Private Cloud now runs completely as Kubernetes cluster. See the [Helm example repo](https://github.com/nrwl/nx-cloud-helm) for more details
### 05-08-2022T15-42-20
- Fix: issue with retrieving hashes during reads
- Feat: added route to display container version at `/version`
- Misc: forward api errors to stderr so k8s clusters can process them better
### 02-08-2022T16-11-36
- Note: The version naming scheme for the containers was changed to better track date/time of releases and to support embedding of the version inside the web UI
- Feat: view the container version under the `/errors` route
- Feat: BitBucket login (note: does not support self-hosted instances of BitBucket Server)
- Feat: New system-ui font scheme
- Fix: branch screen sorting performance improvements
### 2.4.11
- Fixes an intermittent container start-up issue when running a self-contained Mongo instance
- Fixes an issue with the self-hosted file-server where it would fail to create the initial directories
### 2.4.10
- Fix an issue with the admin password not being set correctly
### 2.4.9
- Align all Nx Cloud images to this version. No new fixes or features included.
### 2.4.8
{% callout type="caution" title="IMPORTANT" %}
The default container mode has changed from `COMMUNITY` to `ENTERPRISE`. If you are running a Community version of the container, you will need to make sure the `NX_CLOUD_MODE=private-community` is explicitly set (otherwise your container will fail to start-up).
{% /callout %}
- Fix: Web app performance improvements
- Fix: issue with GitHub logged in admins not being able to download logs
- Fix: issue with billing page when multiple access tokens were attached to the same org
- Fix: multiple Mongo DBs used to be created if a default DB was not provided in the connection string. Now it always defaults to the provided `NX_CLOUD_MONGO_DB_NAME`
### 2.4.7
- Misc: performance improvements to DB indexes
- Misc: improvements to hash differ to use regex
- Misc: export more collections for debug purposes (workspaces and organizations)
### 2.4.6
- Fix: issue with navigating to organizations/workspaces in the web app
### 2.4.5
- Feat: filters to branch and run list pages
- Fix: improved `MD5` cache artifact archiving
- Misc: various UI and UX improvements to the Nx Cloud dashboards
### 2.4.4
- Fix: Missing artefact retrieval error when using read-tokens
- Fix: Performance improvements to the branch page and run groups sorting
- Fix: better handling of artefact `.tar` archiving
### 2.4.3
- Feat: Billing page messaging improvements
- Fix: runs sorting on branch page
### 2.4.2
- Feat: DTE post-run report
- Feat: Hash Detail tool flow improvements
### 2.4.1
- Feat: Admins can now easily export debug info for error investigation
- Fix: branch screen run group sorting
### 2.4.0
- Feat: [GitLab Auth Support](https://nx.app/docs/private-cloud-gitlab-auth)/private-cloud-gitlab-auth
- Feat: Hash diffing tool improvements
- Feat: show message on branch page if workspace is unclaimed
- Fix: Agent out of memory warning
- Feat: cache inner runs
- Fix: include correct GitHub workflows path
- Fix: default to most recent run group on branch page
- Fix: handle DTEs with no tasks
- Fix: await process checkout sessions
### 2.3.1
- Feat: Increase file-server default cached artifact limit. If you are not using an external file storage (such as S3), then the cached assets will now be kept by default from 2 weeks to 4 weeks, increasing the chance of cache hits.
- Feat: "Download cache usage" data from the "Time saved" workspace page
### 2.3.0
- Feat: GitHub Integration - no token is now necessary in "`nx.json`" for the GitHub integration to work (you still need to provide as an env var for caching to work). To connect your workspace to GitHub without an access token in "`nx.json`" just pass in the "`NX_CLOUD_INTEGRATION_DEFAULT_WORKSPACE_ID=<your-workspace-id>`" env var
- Misc: better error handling (report less false positives)
- Fix: Scheduled tasks locking
### 2.2.16
- Misc: DB performance improvements (old records clean-up aggregator, indexes etc.)
### 2.2.15
- Feat: Add options to control database load
- Fix: Better exception handling in the API
### 2.2.14
- Feat: Optimize event processing to increase the throughput of workspaces with a very high number of agents.
- Fix: Gracefully recover when stats aggregation fails
### 2.2.13
- Feat: Hash diffing tool enhancements
### 2.2.12
- Feat: DTE visualisation improvements for larger workspaces
- Fix: billing page not displaying subscriptions for Private Community
### 2.2.11
- Feat: Better error handling for scheduled tasks
- Fix: branch screen not loading
### 2.2.10
- Feat: Various UI improvements to the Nx Cloud screens
- Feat: Hash detail diff tool
- Feat: GitHub app comment revamp
- Feat: DTE visualisation
### 2.2.9
- Fix: DTE bug fixes caused by incorrectly batched tasks
### 2.2.8
- Fix: various DTE bug fixes
- Feat: Add `NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT` env var for more explicitly optimising DTEs
- Feat: Send GitHub workspace membership invites by email
- Fix: improve container start-up time
- Feat: If Mongo connection fails during container start-up it keeps retrying up to a max number of times (configurable via `MONGO_MAX_RETRIES`)
- Feat: expose "/ping" endpoint (can be useful for K8s readinessProbe)
- `curl --fail http://localhost:8081/nx-cloud/ping --header "authorization: your-nx-cloud-access-token"`
- Feat: billing estimator (on billing page)
- Fix: ignore ending slash on `NX_CLOUD_APP_URL` (in case it's added by mistake)
### 2.2.7
- Feat: `VERBOSE=1` env variable option to output extra information during container initialisation
- Feat: `MONGO_REPAIR=1` env variable option to trigger a [Mongo Repair](https://docs.mongodb.com/manual/tutorial/recover-data-following-unexpected-shutdown/) if the container data gets corrupted
### 2.2.3
- Fix: Reset the memory limits to best work on an instance with 8GB of RAM.
- Fix: Set the default `NX_CLOUD_MODE` to "community".
### 2.2
- [Nx Cloud 2.2](https://blog.nrwl.io/%EF%B8%8F-nx-cloud-2-2-%EF%B8%8F-b7656ed5ce7c)
### 2.0
- [Overview of Nx Cloud 2.0](https://blog.nrwl.io/introducing-nx-cloud-2-0-f1e5c2002a65)
@@ -1,16 +1,16 @@
---
import {devkitPages } from '../../../utils/devkit-content-queries'
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
import { render } from 'astro:content';
import {devkitPages } from '../../../utils/devkit-content-queries'
export async function getStaticPaths() {
return await devkitPages()
return await devkitPages()
}
const { doc } = Astro.props
const { slug, title} = Astro.params
if(!doc) {
throw new Error(`Missing devkit page for ${title} expected at ${slug}`)
const { doc } = Astro.props;
const { name } = Astro.params;
if (!doc) {
throw new Error(`DevKit doc not found, ${name}`);
}
const { Content, headings } = await render(doc);
@@ -0,0 +1,22 @@
---
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
import { render, getEntry } from 'astro:content';
const devkitDocs = await getEntry('nx-reference-packages', 'devkit-overview');
if (!devkitDocs) {
throw new Error('DevKit documentation not found');
}
const { headings, Content } = await render(devkitDocs);
---
<StarlightPage
frontmatter={{
title: 'DevKit Documentation',
description: 'Documentation for the DevKit'
}}
headings={headings || []}
>
<Content />
</StarlightPage>
@@ -0,0 +1,24 @@
---
import { getCollection, render } from 'astro:content';
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
import {ngcliAdapterPages } from '../../../../utils/devkit-content-queries'
export async function getStaticPaths() {
return await ngcliAdapterPages()
}
const { doc, name } = Astro.props;
if (!doc) {
throw new Error(`ngcli_adapter doc not found. ${name}`);
}
const { Content, headings } = await render(doc);
---
<StarlightPage
frontmatter={{
title: doc.data.title
}}
headings={headings || []}
>
<Content />
</StarlightPage>
@@ -0,0 +1,21 @@
---
import { getEntry, render, getCollection } from 'astro:content';
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
const doc = await getEntry('nx-reference-packages', 'ngcli_adapter-overview');
if (!doc) {
throw new Error('angular cli adapter overview doc not found');
}
const { Content, headings } = await render(doc);
---
<StarlightPage
frontmatter={{
title: doc.data.title
}}
headings={headings || []}
>
<Content />
</StarlightPage>
@@ -54,7 +54,6 @@ async function loadCommunityPluginsData(
id: plugin.name,
collection: 'community-plugins',
data: {
title: plugin.name,
slug: plugin.name,
description: plugin.description,
url: plugin.url,
File diff suppressed because it is too large Load Diff
@@ -1,37 +0,0 @@
import { defineRouteMiddleware } from '@astrojs/starlight/route-data';
/*
* Using this middleware to insert icons because `attrs` is not valid for sidebar items with children.
* This also allows us to add icons to autogenerated sidebar items (if needed in the future).
*/
const iconMap: Record<string, string> = {
TypeScript: 'typescript',
Angular: 'angular',
React: 'react',
Vue: 'vuedotjs',
'Node.js': 'nodedotjs',
Java: 'java',
'Module Federation': 'module-federation',
ESLint: 'eslint',
'Build Tools': 'buildtools',
'Test Tools': 'testtools',
};
function addIconsToSidebar(sidebar: any[]): void {
sidebar.forEach((entry) => {
const icon = iconMap[entry.label];
if (icon) {
entry.attrs = { ...entry.attrs, 'data-icon': icon };
}
if (entry.type === 'group' && entry.entries) {
addIconsToSidebar(entry.entries);
}
});
}
export const onRequest = defineRouteMiddleware(async (context) => {
const { sidebar } = context.locals.starlightRoute;
addIconsToSidebar(sidebar);
});
@@ -2,8 +2,11 @@ import {
defineRouteMiddleware,
type StarlightRouteData,
} from '@astrojs/starlight/route-data';
import { devkitPages } from '../utils/devkit-content-queries';
import { getEntries, getEntry } from 'astro:content';
import {
devkitPages,
ngcliAdapterPages,
} from '../utils/devkit-content-queries';
import { getEntries } from 'astro:content';
interface SidebarLink {
type: 'link';
@@ -40,23 +43,46 @@ export const onRequest = defineRouteMiddleware(async (context) => {
context.locals.starlightRoute
);
const apiPackageSections = await getApiPackageSections(
context.locals.starlightRoute
);
// Apply sorting to reference entries
const newEntries = [...commandSection, ...apiPackageSections, devkitSection];
// Merge new entries with existing entries to preserve any that are already there
const existingEntries = refSection.entries as (SidebarGroup | SidebarLink)[];
// Apply sorting to reference entries
const newEntries = [...commandSection, devkitSection];
refSection.entries = sortReferenceEntries(
existingEntries,
refSection.entries as (SidebarGroup | SidebarLink)[],
newEntries,
desiredSectionOrder
);
});
async function getDevKitSection({ entry }: StarlightRouteData) {
const ngcliAdapterItems = await ngcliAdapterPages();
const ngcliAdapterOverview: SidebarLink = {
type: 'link',
label: 'Overview',
href: '/docs/reference/devkit/ngcli_adapter',
badge: undefined,
isCurrent: entry.slug === 'reference/devkit/ngcli_adapter',
attrs: {},
};
const ngcliAdapterRoutes = ngcliAdapterItems.map(
(record): SidebarLink => ({
type: 'link',
label: record.props.doc.data.title,
href: `/docs/reference/devkit/ngcli_adapter/${record.params.name}`,
badge: undefined,
isCurrent:
entry.slug === `reference/devkit/ngcli_adapter/${record.params.name}`,
attrs: {},
})
);
const ngcliAdapterGroup: SidebarGroup = {
type: 'group',
label: 'Angular CLI Adapter',
entries: [ngcliAdapterOverview, ...ngcliAdapterRoutes],
collapsed: !entry.slug.startsWith('reference/devkit/ngcli_adapter'),
badge: undefined,
};
const devkitOverview: SidebarLink = {
type: 'link',
label: 'Overview',
@@ -68,61 +94,21 @@ async function getDevKitSection({ entry }: StarlightRouteData) {
const devkitItems = await devkitPages();
const devkitOnlyItems = devkitItems.filter(
(item) =>
!item.params.slug?.startsWith('ngcli_adapter') && item.params.slug !== ''
);
const ngcliItems = devkitItems.filter(
(item) =>
item.params.slug?.startsWith('ngcli_adapter/') &&
item.params.slug !== 'ngcli_adapter'
);
const devkitRoutes = devkitOnlyItems.map(
const devkitRoutes = devkitItems.map(
(record): SidebarLink => ({
type: 'link',
label: record.props.doc.data.title,
href: `/docs/reference/devkit/${record.props.doc.data.slug}`,
href: `/docs/reference/devkit/${record.params.name}`,
badge: undefined,
isCurrent:
entry.slug === `reference/devkit/${record.props.doc.data.slug}`,
isCurrent: entry.slug === `reference/devkit/${record.params.name}`,
attrs: {},
})
);
const ngcliOverview: SidebarLink = {
type: 'link',
label: 'Overview',
href: '/docs/reference/devkit/ngcli_adapter',
badge: undefined,
isCurrent: entry.slug === 'reference/devkit/ngcli_adapter',
attrs: {},
};
const ngcliRoutes = ngcliItems.map(
(record): SidebarLink => ({
type: 'link',
label: record.props.doc.data.title,
href: `/docs/reference/devkit/${record.props.doc.data.slug}`,
badge: undefined,
isCurrent:
entry.slug === `reference/devkit/${record.props.doc.data.slug}`,
attrs: {},
})
);
const ngcliSection: SidebarGroup = {
type: 'group',
label: 'ngcli_adapter',
entries: [ngcliOverview, ...ngcliRoutes],
collapsed: !entry.slug.startsWith('reference/devkit/ngcli_adapter'),
badge: undefined,
};
const devkitSection: SidebarGroup = {
type: 'group',
label: 'Devkit',
entries: [devkitOverview, ngcliSection, ...devkitRoutes],
entries: [devkitOverview, ngcliAdapterGroup, ...devkitRoutes],
collapsed: !entry.slug.startsWith('reference/devkit'),
badge: undefined,
};
@@ -149,55 +135,6 @@ async function getCommandsSection({ entry }: StarlightRouteData) {
});
}
async function getApiPackageSections({ entry }: StarlightRouteData) {
const packages = ['nx', 'plugin', 'web', 'workspace'];
const sections: SidebarGroup[] = [];
for (const pkg of packages) {
const entries: SidebarLink[] = [];
// Try to get each doc type for this package
const docTypes = [
{ id: `${pkg}-overview`, label: 'Overview', path: '' },
{ id: `${pkg}-executors`, label: 'Executors', path: '/executors' },
{ id: `${pkg}-generators`, label: 'Generators', path: '/generators' },
{ id: `${pkg}-migrations`, label: 'Migrations', path: '/migrations' },
];
for (const docType of docTypes) {
try {
const doc = await getEntry('nx-reference-packages', docType.id);
if (doc) {
const href = `/docs/reference/${pkg}${docType.path}`;
entries.push({
type: 'link',
label: docType.label,
href,
badge: undefined,
isCurrent: entry.slug === `reference/${pkg}${docType.path}`,
attrs: {},
});
}
} catch (e) {
// Doc doesn't exist, skip
}
}
if (entries.length > 0) {
const packageName = pkg === 'nx' ? 'nx' : `@nx/${pkg}`;
sections.push({
type: 'group',
label: packageName,
entries,
collapsed: !entry.slug.startsWith(`reference/${pkg}`),
badge: undefined,
});
}
}
return sections;
}
function sortReferenceEntries(
existingEntries: (SidebarLink | SidebarGroup)[],
newEntries: (SidebarLink | SidebarGroup)[],
@@ -1,190 +0,0 @@
import { workspaceRoot } from '@nx/devkit';
import type { LoaderContext } from 'astro/loaders';
import type { CollectionEntry } from 'astro:content';
import { fork } from 'node:child_process';
import { join, relative } from 'node:path';
export interface ParsedCommandOption {
name: string[];
type: string;
description: string;
default: any;
deprecated: boolean | string;
hidden: boolean;
choices?: string[];
}
export interface ParsedCommand {
name: string;
commandString: string;
description: string;
options?: ParsedCommandOption[];
}
export async function loadCnwPackage(
context: LoaderContext
): Promise<CollectionEntry<'nx-reference-packages'>> {
const { logger, renderMarkdown } = context;
logger.info('🔍 Loading Create Nx Workspace documentation...');
const subprocessPath = join(
workspaceRoot,
'astro-docs/src/plugins/utils/cnw-subprocess.cjs'
);
const result = await new Promise<{
command: ParsedCommand;
presets: string[];
presetDescriptions: Record<string, string>;
}>((resolve, reject) => {
const child = fork(subprocessPath, [], {
cwd: workspaceRoot,
silent: true,
});
let stdout = '';
let stderr = '';
child.stdout?.on('data', (data) => {
stdout += data.toString();
});
child.stderr?.on('data', (data) => {
stderr += data.toString();
logger.warn(data.toString());
});
child.on('message', (message: any) => {
if (message.type === 'result') {
resolve(message.data);
} else if (message.type === 'error') {
reject(new Error(message.error));
}
});
child.on('error', (error) => {
reject(error);
});
child.on('exit', (code) => {
if (code !== 0) {
reject(
new Error(
`CNW subprocess exited with code ${code}\nstderr: ${stderr}`
)
);
}
});
});
const markdown = generateCNWMarkdown(
result.command,
result.presets,
result.presetDescriptions
);
logger.info('✅ Loaded Create Nx Workspace documentation');
const entry: CollectionEntry<'nx-reference-packages'> = {
id: 'cnw-cli',
body: markdown,
filePath: relative(
join(workspaceRoot, 'astro-docs'),
join(
workspaceRoot,
'packages/create-nx-workspace/bin/create-nx-workspace.ts'
)
),
data: {
title: 'create-nx-workspace',
slug: 'reference/create-nx-workspace',
packageType: 'cnw',
docType: 'cli',
description: 'Create a new Nx workspace',
},
// @ts-expect-error - astro types are mismatched bc of auto generated location loading, etc
rendered: await renderMarkdown(markdown),
};
return entry;
}
function generateCNWMarkdown(
command: ParsedCommand,
presets: string[],
presetDescriptions: Record<string, string>
): string {
let content = `
${command.description}
## Usage
\`\`\`bash
npx ${command.commandString}
\`\`\`
`;
// Add options table
if (command.options && command.options.length > 0) {
content += '## Options\n\n';
content += '| Option | Type | Description |\n';
content += '| ----------- | ----------- | ---------- |\n';
const sortedOptions = command.options
.filter((option) => !option.hidden)
.sort((a, b) => a.name[0].localeCompare(b.name[0]));
for (const option of sortedOptions) {
const optionNames = option.name
.map((n, index) => {
if (index === 0) {
return `\`--${n}\``;
} else {
return n.length === 1 ? `\`--${n}\`` : `\`--${n}\``;
}
})
.join(', ');
let description = option.description || '';
if (option.deprecated) {
description += ` **⚠️ Deprecated**${
option.deprecated !== true ? `: ${option.deprecated}` : ''
}`;
}
let type = option.type;
if (option.choices && option.choices.length > 0) {
type = option.choices.map((c) => `\`${c}\``).join(', ');
}
if (option.default !== undefined) {
description += ` (Default: \`${JSON.stringify(option.default).replace(
/"/g,
''
)}\`)`;
}
content += `| ${optionNames} | ${type} | ${
description || '_No Description_'
} |\n`;
}
content += '\n';
}
// Add presets table
content += '## Presets\n\n';
content += '| Preset | Description |\n';
content += '| ---------- | ----------|\n';
const sortedPresets = presets.sort();
for (const preset of sortedPresets) {
const description =
presetDescriptions[preset] || 'No description available';
content += `| ${preset} | ${description} |\n`;
}
return content;
}
@@ -1,161 +0,0 @@
import { workspaceRoot } from '@nx/devkit';
import type { LoaderContext } from 'astro/loaders';
import { existsSync, readFileSync } from 'fs';
import { join } from 'path';
import {
getGeneratorsMarkdown,
getExecutorsMarkdown,
getMigrationsMarkdown,
} from './generate-plugin-markdown';
import {
parseGenerators,
parseExecutors,
parseMigrations,
} from './plugin-schema-parser';
import {
getGithubStars,
shouldFetchStats,
getNpmDownloads,
getNpmData,
} from './plugin-stats';
import type { CollectionEntry } from 'astro:content';
// TODO(caleb): make this function not specific to these packages AND work with the plugin.loader.ts file
export async function loadNxSpecialPackage(
packageName: 'nx' | 'plugin' | 'web' | 'workspace',
context: LoaderContext
): Promise<CollectionEntry<'nx-reference-packages'>[]> {
const { logger, renderMarkdown } = context;
const entries: CollectionEntry<'nx-reference-packages'>[] = [];
logger.info(`Loading ${packageName} package documentation...`);
const pluginPath = join(workspaceRoot, 'packages', packageName);
if (!existsSync(pluginPath)) {
logger.warn(`Package ${packageName} path does not exist`);
return [];
}
const packageJsonPath = join(pluginPath, 'package.json');
let packageDescription = `The Nx ${packageName} package`;
try {
if (existsSync(packageJsonPath)) {
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
if (packageJson.description && packageJson.description.trim()) {
packageDescription = packageJson.description.trim();
}
}
} catch (error) {
// Fall back to default description
}
const ghStarMap = await getGithubStars([{ owner: 'nrwl', repo: 'nx' }]);
const npmPackageName = packageName === 'nx' ? 'nx' : `@nx/${packageName}`;
// Create overview entry
const overviewEntry: CollectionEntry<'nx-reference-packages'> = {
id: `${packageName}-overview`,
collection: 'nx-reference-packages',
data: {
title: npmPackageName,
slug: `reference/${packageName}`,
packageType: packageName,
docType: 'overview',
description: packageDescription,
features: [],
totalDocs: 0,
githubStars: ghStarMap.get('nrwl/nx')?.stargazers?.totalCount || 0,
},
};
// Process generators
const generators = parseGenerators(pluginPath);
if (generators && generators.size > 0) {
const markdown = getGeneratorsMarkdown(packageName, generators);
entries.push({
id: `${packageName}-generators`,
body: markdown,
// @ts-expect-error - astro types are mismatched bc of auto generated location loading, etc
rendered: await renderMarkdown(markdown),
collection: 'nx-reference-packages',
data: {
title: `${npmPackageName} Generators`,
slug: `reference/${packageName}/generators`,
packageType: packageName,
docType: 'generators',
description: packageDescription,
},
});
overviewEntry.data.features!.push('generators');
overviewEntry.data.totalDocs!++;
}
// Process executors
const executors = parseExecutors(pluginPath);
if (executors && executors.size > 0) {
const markdown = getExecutorsMarkdown(packageName, executors);
entries.push({
id: `${packageName}-executors`,
body: markdown,
// @ts-expect-error - astro types are mismatched bc of auto generated location loading, etc
rendered: await renderMarkdown(markdown),
collection: 'nx-reference-packages',
data: {
title: `${npmPackageName} Executors`,
slug: `reference/${packageName}/executors`,
packageType: packageName,
docType: 'executors',
description: packageDescription,
},
});
overviewEntry.data.features!.push('executors');
overviewEntry.data.totalDocs!++;
}
// Process migrations
const migrations = parseMigrations(pluginPath);
if (migrations && migrations.size > 0) {
const markdown = getMigrationsMarkdown(packageName, migrations);
entries.push({
id: `${packageName}-migrations`,
body: markdown,
// @ts-expect-error - astro types are mismatched bc of auto generated location loading, etc
rendered: await renderMarkdown(markdown),
collection: 'nx-reference-packages',
data: {
title: `${npmPackageName} Migrations`,
slug: `reference/${packageName}/migrations`,
packageType: packageName,
docType: 'migrations',
description: packageDescription,
},
});
overviewEntry.data.features!.push('migrations');
overviewEntry.data.totalDocs!++;
}
// Fetch npm stats if needed
const existingOverviewEntry = context.store.get<
CollectionEntry<'nx-reference-packages'>['data']
>(`${packageName}-overview`);
if (shouldFetchStats(existingOverviewEntry)) {
const npmPackage = {
name: npmPackageName,
url: `https://github.com/nrwl/nx/tree/master/packages/${packageName}`,
description: packageDescription,
};
const npmDownloads = await getNpmDownloads(npmPackage);
const npmMeta = await getNpmData(npmPackage);
overviewEntry.data.npmDownloads = npmDownloads;
overviewEntry.data.lastPublishedDate = npmMeta.lastPublishedDate;
overviewEntry.data.lastFetched = new Date();
}
entries.push(overviewEntry);
logger.info(`✅ Loaded ${packageName} package documentation`);
return entries;
}
@@ -1,173 +0,0 @@
import { type CollectionEntry } from 'astro:content';
import {
setupTypeDoc,
runTypeDoc,
directoryToCategoryMap,
} from './typedoc/typedoc';
import type { LoaderContext } from 'astro/loaders';
import { workspaceRoot } from '@nx/devkit';
import { join } from 'node:path';
import { existsSync } from 'node:fs';
import { readFile, readdir } from 'node:fs/promises';
export async function loadDevkitPackage(
context: LoaderContext
): Promise<CollectionEntry<'nx-reference-packages'>[]> {
const { logger, renderMarkdown } = context;
logger.info('Loading DevKit documentation');
const { defaultTypedocOptions, outDir, buildDir } = setupTypeDoc(logger);
const entries: CollectionEntry<'nx-reference-packages'>[] = [];
// TODO: Caleb there seems to be a resolution error where this entrypoint will resolved types
// from the node_modules/nx package and not the local workspace changes
// see: DOC-188
logger.info('Generating devkit docs to dir...');
// generate main @nx/devkit docs
const devkitEntryPoint = join(
workspaceRoot,
'dist',
'packages',
'devkit',
'index.d.ts'
);
if (existsSync(devkitEntryPoint)) {
await runTypeDoc(
{
...defaultTypedocOptions,
entryPoints: [devkitEntryPoint],
tsconfig: join(buildDir, 'tsconfig.lib.json'),
out: outDir,
excludePrivate: true,
publicPath: '/docs/reference/devkit/',
},
logger
);
}
logger.info('Generating devkit/ngcli_adapter docs...');
// generate ngcli docs in same dir
const ngcliEntryPoint = join(
workspaceRoot,
'dist',
'packages',
'devkit',
'ngcli-adapter.d.ts'
);
if (existsSync(ngcliEntryPoint)) {
await runTypeDoc(
{
...defaultTypedocOptions,
entryPoints: [ngcliEntryPoint],
tsconfig: join(buildDir, 'tsconfig.lib.json'),
out: join(outDir, 'ngcli_adapter'),
publicPath: '/docs/reference/devkit/ngcli_adapter/',
},
logger
);
}
logger.info(`Loading devkit docs from ${outDir}`);
const markdownFiles = await walkDirectory(outDir);
for (const filePath of markdownFiles) {
// Get the relative path from the output directory
const relativePath = filePath.replace(outDir + '/', '');
// Get the title from the filename (last part without .md)
const pathParts = relativePath.split('/');
const fileName = pathParts[pathParts.length - 1];
let slug = '';
let title = '';
let category = '';
// Handle README.md files as overview routes
if (fileName === 'README.md') {
if (pathParts.length === 1) {
// Root README becomes 'overview' 'devkit' index route
slug = '';
title = '@nx/devkit Overview';
category = 'overview';
} else if (pathParts.length === 2 && pathParts[0] === 'ngcli_adapter') {
// ngcli_adapter/README becomes 'devkit/ngcli_adapter' index route
slug = 'ngcli_adapter';
title = 'ngcli_adapter Overview';
category = 'overview';
}
} else {
// Flatten routes: remove intermediate directory structure
// For ngcli_adapter subroutes, keep the ngcli_adapter prefix but flatten the rest
if (pathParts[0] === 'ngcli_adapter' && pathParts.length > 2) {
// ngcli_adapter/classes/SomeFile.md -> ngcli_adapter/SomeFile
slug = `ngcli_adapter/${fileName.replace(/\.md$/, '')}`;
} else if (pathParts[0] !== 'ngcli_adapter' && pathParts.length > 1) {
// classes/SomeFile.md -> SomeFile
slug = fileName.replace(/\.md$/, '');
} else {
// Already at root level or single directory
slug = relativePath.replace(/\.md$/, '');
}
title = fileName.replace(/\.md$/, '');
category = pathParts.length > 1 ? pathParts[0] : 'overview';
}
// Read the markdown content
let content = await readFile(filePath, 'utf-8');
// // Remove .md extensions from all links in the content
if (content) {
// Remove .md from markdown links: [text](path.md) -> [text](path)
content = content
.replace(/(\[.*?\]\([^)]*?)\.md(\)|#)/gi, '$1$2')
// Remove .md from any remaining URLs that end with .md
.replace(/\.md(?=[#)\s]|$)/gim, '');
}
const rendered = content ? await renderMarkdown(content) : undefined;
const mappedCategory = directoryToCategoryMap[category] || category;
const documentRecord: CollectionEntry<'nx-reference-packages'> = {
id: `devkit_${slug.replace(/\//g, '-')}`,
body: content,
// @ts-expect-error astro auto gen types don't align
rendered,
collection: 'nx-reference-packages',
data: {
title: title,
packageType: 'devkit',
docType: 'devkit',
slug,
category: mappedCategory,
},
};
entries.push(documentRecord);
}
return entries;
}
/**
* Recursively walk a directory and return all .md file paths
*/
async function walkDirectory(dir: string): Promise<string[]> {
const files: string[] = [];
const entries = await readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = join(dir, entry.name);
if (entry.isDirectory()) {
// Recursively walk subdirectories
const subFiles = await walkDirectory(fullPath);
files.push(...subFiles);
} else if (entry.isFile() && entry.name.endsWith('.md')) {
files.push(fullPath);
}
}
return files;
}
@@ -1,186 +0,0 @@
import { workspaceRoot } from '@nx/devkit';
import type { LoaderContext } from 'astro/loaders';
import type { CollectionEntry } from 'astro:content';
import { fork } from 'node:child_process';
import { join, relative } from 'node:path';
export interface ParsedCliCommand {
name?: string;
command?: string;
description?: string;
aliases?: string[];
options?: Array<{
name: string[];
type: string;
description: string;
default?: any;
deprecated?: boolean | string;
choices?: string[];
}>;
}
export async function loadNxCliPackage(
context: LoaderContext
): Promise<CollectionEntry<'nx-reference-packages'>> {
const { logger, renderMarkdown } = context;
logger.info('🔍 Loading Nx CLI documentation...');
const subprocessPath = join(
workspaceRoot,
'astro-docs/src/plugins/utils/cli-subprocess.cjs'
);
const result = await new Promise<{
commands: Record<string, ParsedCliCommand>;
}>((resolve, reject) => {
const child = fork(subprocessPath, [], {
cwd: workspaceRoot,
silent: true,
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
});
child.on('message', (message: any) => {
if (message.type === 'result') {
resolve(message.data);
} else if (message.type === 'error') {
reject(new Error(message.error));
}
child.send({ type: 'stop' });
});
child.on('error', (error) => {
reject(error);
});
child.on('exit', (code) => {
if (code !== 0) {
reject(new Error(`CLI subprocess exited with code ${code}`));
}
});
child.send({ type: 'start' });
});
const markdown = generateCLIMarkdown(result.commands);
logger.info(
`✅ Loaded CLI documentation with ${
Object.keys(result.commands).length
} commands`
);
const entry: CollectionEntry<'nx-reference-packages'> = {
id: 'nx-cli',
body: markdown,
filePath: relative(
join(workspaceRoot, 'astro-docs'),
join(workspaceRoot, 'packages/nx/src/command-line/nx-commands.ts')
),
data: {
title: 'Nx Commands',
slug: 'reference/nx-commands',
packageType: 'nx-cli',
docType: 'cli',
description: 'Complete reference for Nx CLI commands',
},
// @ts-expect-error - astro types are mismatched bc of auto generated location loading, etc
rendered: await renderMarkdown(markdown),
collection: 'nx-reference-packages',
};
return entry;
}
function generateCLIMarkdown(
commands: Record<string, ParsedCliCommand>
): string {
const commandNames = Object.keys(commands).sort();
const content = `
The Nx command line has various subcommands and options to help you manage your Nx workspace and run tasks efficiently.
Below is a complete reference for all available commands and their options.
You can run nx --help to view all available options.
## Available Commands
${commandNames
.map((cmdName) => {
const cmd = commands[cmdName];
let section = `### \`nx ${cmdName}\`\n`;
section += cmd.description || 'No description available';
if (cmd.aliases && cmd.aliases.length > 0) {
section += `**Aliases:** ${cmd.aliases
.map((alias) => `\`${alias}\``)
.join(', ')}`;
}
section += `\n\n**Usage:**
\`\`\`bash
nx ${cmd.command || cmdName}
\`\`\`
`;
// Add options table if there are options
if (cmd.options && cmd.options.length > 0) {
section += '\n#### Options\n\n';
section += '| Option | Type | Description | Default |\n';
section += '|--------|------|-------------|---------|\n';
const sortedOptions = cmd.options.sort((a, b) =>
a.name[0].localeCompare(b.name[0])
);
for (const option of sortedOptions) {
const optionNames = option.name.map((n) => `\`--${n}\``).join(', ');
let description = option.description || '';
if (option.name.length > 1) {
const aliases = option.name
.slice(1)
.map((a) => `\`-${a}\``)
.join(', ');
description += ` (alias: ${aliases})`;
}
if (option.deprecated) {
description += ` **⚠️ Deprecated**${
option.deprecated !== true ? `: ${option.deprecated}` : ''
}`;
}
if (option.choices && option.choices.length > 0) {
description += ` (choices: ${option.choices
.map((c) => `\`${c}\``)
.join(', ')})`;
}
const defaultValue =
option.default !== undefined
? `\`${JSON.stringify(option.default).replace(/"/g, '')}\``
: '';
section += `| ${optionNames} | ${option.type} | ${
description || '_No Description_'
} | ${defaultValue} |\n`;
}
section += '\n';
}
return section;
})
.join('\n')}
## Getting Help
You can get help for any command by adding the \`--help\` flag:
\`\`\`bash
nx <command> --help
\`\`\`
`;
return content;
}
@@ -0,0 +1,51 @@
import * as Handlebars from 'handlebars';
import type { CommentDisplayPart } from 'typedoc';
export default function () {
Handlebars.registerHelper('comment', function (parts: CommentDisplayPart[]) {
const result: string[] = [];
for (const part of parts) {
switch (part.kind) {
case 'text':
case 'code':
result.push(part.text);
break;
case 'inline-tag':
switch (part.tag) {
case '@label':
case '@inheritdoc':
break;
case '@link':
case '@linkcode':
case '@linkplain': {
if (part.target) {
const url =
typeof part.target === 'string'
? part.target
: Handlebars.helpers.relativeURL((part.target as any).url);
const wrap = part.tag === '@linkcode' ? '`' : '';
result.push(
url ? `[${wrap}${part.text}${wrap}](${url})` : part.text
);
} else {
result.push(part.text);
}
break;
}
default:
result.push(`{${part.tag} ${part.text}}`);
break;
}
break;
default:
result.push('');
}
}
return result
.join('')
.split('\n')
.filter((line) => !line.startsWith('@note'))
.join('\n');
});
}
+15 -24
View File
@@ -6,44 +6,36 @@ import {
type RenderTemplate,
} from 'typedoc';
import { MarkdownTheme } from 'typedoc-plugin-markdown/dist/theme';
import comment from './comment';
import toc from './toc';
/**
* The MarkdownTheme is based on TypeDoc's DefaultTheme @see https://github.com/TypeStrong/typedoc/blob/master/src/lib/output/themes/DefaultTheme.ts.
* - html specific components are removed from the renderer
* - markdown specefic components have been added
*/
export default class NxMarkdownTheme extends MarkdownTheme {
constructor(renderer: Renderer) {
super(renderer);
// NOTE: removing this still has the ToC showing up on each page?
// toc(this);
toc(this);
comment();
}
render(
page: PageEvent<Reflection>,
template: RenderTemplate<PageEvent<Reflection>>
): string {
let content = super.render(page, template);
// Remove type-specific directories from links to flatten URL structure
// e.g., /docs/reference/devkit/enums/ChangeType.md -> /docs/reference/devkit/ChangeType.md
content = content
// Remove type directories (enums, classes, interfaces, types, variables, functions) from URLs
.replace(
/(\[.*?\]\([^)]*?\/devkit\/)(?:enums|classes|interfaces|types|variables|functions)\//gi,
'$1'
)
// Handle ngcli_adapter paths - keep the ngcli_adapter prefix but remove type directories
.replace(
/(\[.*?\]\([^)]*?\/devkit\/ngcli_adapter\/)(?:enums|classes|interfaces|types|variables|functions)\//gi,
'$1'
)
// Remove .md extensions from all links
.replace(/(\[.*?\]\([^)]*?)\.md(\)|#)/gi, '$1$2')
// Also handle any remaining .md extensions that might be in URLs
.replace(/\.md(?=[#)]|$)/gi, '');
return content;
return (
super
.render(page, template)
.replace(/\.md/gi, '')
/**
* Hack: This is the simplest way to update the urls and make them work
* in the `/packages/[name]/documents/[index|ngcli_adapter] context.
*/
.replace(/\/devkit\//gi, '/devkit/documents/')
);
}
get mappings() {
@@ -101,7 +93,6 @@ export default class NxMarkdownTheme extends MarkdownTheme {
* Returns the full url of a given mapping and reflection
*/
toUrl(mapping: Record<string, unknown>, reflection: Reflection) {
// Keep .md for actual file generation
return (
(mapping.directory === '.' ? '' : mapping.directory + '/') +
this.getUrl(reflection) +
@@ -1,145 +0,0 @@
import { workspaceRoot } from '@nx/devkit';
import type { LoaderContext } from 'astro/loaders';
import {
cpSync,
existsSync,
mkdirSync,
readFileSync,
rmSync,
writeFileSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import {
Application,
PackageJsonReader,
TSConfigReader,
TypeDocReader,
type TypeDocOptions,
} from 'typedoc';
import NxMarkdownTheme from './theme';
// Map directory names to categories
export const directoryToCategoryMap: Record<string, string> = {
classes: 'Classes',
enums: 'Enumerations',
functions: 'Functions',
interfaces: 'Interfaces',
types: 'Type Aliases',
variables: 'Variables',
};
export function setupTypeDoc(logger: LoaderContext['logger']) {
const tempDir = join(tmpdir(), `nx-devkit-docs`);
const projectRoot = process.cwd();
const buildDir = join(workspaceRoot, 'dist', 'packages', 'devkit');
const outDir = join(tempDir, 'docs', 'generated', 'devkit');
mkdirSync(buildDir, { recursive: true });
mkdirSync(outDir, { recursive: true });
mkdirSync(join(tempDir, 'packages', 'devkit'), { recursive: true });
const devkitPath = join(workspaceRoot, 'packages', 'devkit');
const tsconfigLibPath = join(devkitPath, 'tsconfig.lib.json');
const tsconfigPath = join(devkitPath, 'tsconfig.json');
const tsconfigBasePath = join(workspaceRoot, 'tsconfig.base.json');
if (!existsSync(tsconfigLibPath)) {
logger.warn(
'tsconfig.lib.json not found, skipping DevKit documentation generation'
);
throw new Error(
`tsconfig.lib.json not found, unable to generate docs. ${tsconfigLibPath}`
);
}
cpSync(tsconfigLibPath, join(buildDir, 'tsconfig.lib.json'));
if (existsSync(tsconfigPath)) {
cpSync(tsconfigPath, join(tempDir, 'packages', 'devkit', 'tsconfig.json'));
}
if (existsSync(tsconfigBasePath)) {
cpSync(tsconfigBasePath, join(tempDir, 'tsconfig.base.json'));
}
let tsconfigContent = readFileSync(
join(buildDir, 'tsconfig.lib.json'),
'utf-8'
);
const tsconfigObj = JSON.parse(tsconfigContent);
// remap to generated tsconfig to resolve correct local packages
if (tsconfigObj.extends === '../../tsconfig.base.json') {
tsconfigObj.extends = join(tempDir, 'packages', 'devkit', 'tsconfig.json');
}
tsconfigObj.compilerOptions = tsconfigObj.compilerOptions || {};
tsconfigObj.compilerOptions.rootDir = projectRoot;
tsconfigObj.compilerOptions.typeRoots = [
join(projectRoot, 'node_modules', '@types'),
];
tsconfigObj.exclude = [
...(tsconfigObj.exclude || []),
'**/*.spec.ts',
'**/*.test.ts',
'**/test/**',
'**/tests/**',
'node_modules/@types/jasmine/**',
'node_modules/@types/jest/**',
];
writeFileSync(
join(buildDir, 'tsconfig.lib.json'),
JSON.stringify(tsconfigObj, null, 2)
);
rmSync(outDir, { recursive: true, force: true });
const defaultTypedocOptions: Partial<TypeDocOptions> & {
[key: string]: unknown;
} = {
plugin: ['typedoc-plugin-markdown'],
disableSources: true,
theme: 'nx-markdown-theme',
readme: 'none',
hideBreadcrumbs: true,
// Disable automatic H1 generation this is done via astro now
hidePageTitle: true,
allReflectionsHaveOwnDocument: true,
skipErrorChecking: true,
compilerOptions: {
skipLibCheck: true,
skipDefaultLibCheck: true,
noEmit: true,
},
};
return {
projectRoot,
outDir,
buildDir,
defaultTypedocOptions,
};
}
export async function runTypeDoc(
options: Partial<TypeDocOptions> & { [key: string]: unknown },
logger: LoaderContext['logger']
) {
const app = await Application.bootstrapWithPlugins(
options as Partial<TypeDocOptions>,
[new TypeDocReader(), new PackageJsonReader(), new TSConfigReader()]
);
app.renderer.defineTheme('nx-markdown-theme', NxMarkdownTheme);
const project = await app.convert();
if (!project) {
throw new Error('Failed to convert the project');
}
const outputDir = app.options.getValue('out');
logger.info(`Generating typedoc files to ${outputDir}`);
await app.generateDocs(project, outputDir);
}
+2 -2
View File
@@ -96,8 +96,8 @@
/* Custom font for code blocks */
@font-face {
font-family: 'Input Mono';
src: url('/docs/fonts/InputMono-Regular.woff2') format('woff2'),
url('/docs/fonts/InputMono-Regular.woff') format('woff');
src: url('/fonts/InputMono-Regular.woff2') format('woff2'),
url('/fonts/InputMono-Regular.woff') format('woff');
font-weight: normal;
font-style: normal;
font-display: swap;
+30 -12
View File
@@ -1,19 +1,37 @@
export async function ngcliAdapterPages() {
const { getCollection } = await import('astro:content');
const docs = await getCollection(
'nx-reference-packages',
// we don't want the overview page, this is custom handled via the index.astro page
(doc) =>
doc.id !== 'ngcli_adapter-overview' &&
doc.data.docType === 'ngcli_adapter' &&
doc.data.packageType === 'devkit'
);
return docs.map((doc) => ({
params: { name: doc.id.split('ngcli_adapter_')[1] },
props: {
doc,
},
}));
}
export async function devkitPages() {
const { getCollection } = await import('astro:content');
const docs = await getCollection(
'nx-reference-packages',
(doc) => doc.data.packageType === 'devkit'
// we don't want the overview page, this is custom handled via the index.astro page
(doc) =>
doc.id !== 'devkit-overview' &&
doc.data.docType === 'devkit' &&
doc.data.packageType === 'devkit'
);
const dkRoutes = docs.map((doc) => {
const { title, slug } = doc.data;
return {
params: { title, slug },
props: {
doc,
},
};
});
return dkRoutes;
return docs.map((doc) => ({
params: { name: doc.id.split('devkit_')[1] },
props: {
doc,
},
}));
}
-16
View File
@@ -1,16 +0,0 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"allowJs": true,
"outDir": "out-tsc/playwright",
"sourceMap": false
},
"include": ["e2e/**/*.ts", "e2e/**/*.js", "playwright.config.ts"],
"exclude": [
"out-tsc",
"test-output",
"eslint.config.js",
"eslint.config.mjs",
"eslint.config.cjs"
]
}
-3
View File
@@ -28,9 +28,6 @@
},
{
"path": "../nx-dev/ui-common"
},
{
"path": "./tsconfig.e2e.json"
}
]
}
+3
View File
@@ -10,6 +10,9 @@ const ignoredLinks = [
// TODO: caleb make this nx api reference page
'/docs/reference/nx/executors',
'NxPowerpack-Trial-v1.1.pdf',
// Known issues with devkit type gen atm: see DOC-63
'devkit/tasksRunnerOptions',
'devkit/GenerateFilesOptions',
];
// These are more so until we cut over and can modify production file links
@@ -17,4 +17,4 @@ Presented by James Henry
For better or worse, were living in the era of the AI agent. These tools bring with them the capacity to push more code than ever before… but what happens then? With AI contributions, you require more oversight, more testing, and more automated quality checks in order to scale efficiently. For many teams, this inevitably leads to CI becoming a bottleneck. How many times have you felt frustrated that your work was effectively done, but youve had to spend the next several hours babysitting it to get it in a mergeable state? At Nx, we track this critical but often-overlooked element of software development as your “Time to Green” (TTG). And were not just tracking it, weve shipped a bunch of killer features to help bring that TTG way, way down.
{% call-to-action title="Download the recording" url="https://go.nx.dev/aug2025-webinar" description="Sign up to gain access" /%}
{% call-to-action title="Register today!" url="https://go.nx.dev/aug2025-webinar" description="Save your spot" /%}
@@ -1,23 +0,0 @@
---
title: 'Making the Case for Smarter Monorepos, and How to Not Get Fooled by Myths'
description: 'Join us for our September webinar to learn what monorepo development looks like in 2025, the benefits of smarter monorepos, how to get buy-in from your leadership and teams, and how Nx makes monorepo development easier and even more powerful.'
date: 2025-09-24
slug: 'making-the-case-for-smarter-monorepos-and-how-to-not-get-fooled-by-myths'
authors: ['Miroslav Jonas']
tags: [webinar]
cover_image: /blog/images/2025-09-24/Sept-2025-Webinar-Card.avif
time: 2pm ET/6pm UTC
status: Upcoming
registrationUrl: https://go.nx.dev/sept2025-webinar
---
**Sep 24, 2025 - 2pm ET/6pm UTC**
Presented by Miroslav Jonas
Monorepos and monorepo tooling have changed a lot in the last few years, adapting to the practical needs of large teams. As development environments have become more complex, with increasing demands for scalability and higher production with fewer resources, modern monorepos can be an extremely effective way to bring order to this chaos.
Join us for our September webinar to learn what monorepo development looks like in 2025, the benefits of smarter monorepos, how to get buy-in from your leadership and teams,
and how Nx makes monorepo development easier and even more powerful.
{% call-to-action title="Register today!" url="https://go.nx.dev/sept2025-webinar" description="Save your spot" /%}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 614 KiB

-8
View File
@@ -4719,14 +4719,6 @@
"children": [],
"isExternal": false,
"disableCollapsible": false
},
{
"id": "merge-reports",
"path": "/technologies/test-tools/playwright/api/executors/merge-reports",
"name": "merge-reports",
"children": [],
"isExternal": false,
"disableCollapsible": false
}
],
"isExternal": false,
-9
View File
@@ -3851,15 +3851,6 @@
"originalFilePath": "/packages/playwright/src/executors/playwright/schema.json",
"path": "/technologies/test-tools/playwright/api/executors/playwright",
"type": "executor"
},
"/technologies/test-tools/playwright/api/executors/merge-reports": {
"description": "Merge Playwright blob reports to produce unified reports for the configured reporters (excluding the `blob` reporter).",
"file": "generated/packages/playwright/executors/merge-reports.json",
"hidden": true,
"name": "merge-reports",
"originalFilePath": "/packages/playwright/src/executors/merge-reports/schema.json",
"path": "/technologies/test-tools/playwright/api/executors/merge-reports",
"type": "executor"
}
},
"generators": {
-9
View File
@@ -4183,15 +4183,6 @@
"originalFilePath": "/packages/playwright/src/executors/playwright/schema.json",
"path": "playwright/executors/playwright",
"type": "executor"
},
{
"description": "Merge Playwright blob reports to produce unified reports for the configured reporters (excluding the `blob` reporter).",
"file": "generated/packages/playwright/executors/merge-reports.json",
"hidden": true,
"name": "merge-reports",
"originalFilePath": "/packages/playwright/src/executors/merge-reports/schema.json",
"path": "playwright/executors/merge-reports",
"type": "executor"
}
],
"generators": [
@@ -1,32 +0,0 @@
{
"name": "merge-reports",
"implementation": "/packages/playwright/src/executors/merge-reports/merge-reports.impl.ts",
"schema": {
"$schema": "https://json-schema.org/schema",
"version": 2,
"title": "Schema for Playwright Merge Reports Executor",
"description": "Merge Playwright blob reports to produce unified reports for the configured reporters (excluding the `blob` reporter).",
"type": "object",
"properties": {
"config": {
"description": "The Playwright configuration file path. Relative to the project root.",
"type": "string",
"x-completion-type": "file",
"x-completion-glob": "playwright?(*)@(.js|.cjs|.mjs|.ts|.cts|.mtx)",
"x-priority": "important"
},
"expectedSuites": {
"description": "The expected number of test suites to produce a report.",
"type": "number",
"x-priority": "important"
}
},
"required": ["config"],
"presets": []
},
"description": "Merge Playwright blob reports to produce unified reports for the configured reporters (excluding the `blob` reporter).",
"hidden": true,
"aliases": [],
"path": "/packages/playwright/src/executors/merge-reports/schema.json",
"type": "executor"
}
-1
View File
@@ -569,7 +569,6 @@
- [API](/technologies/test-tools/playwright/api)
- [executors](/technologies/test-tools/playwright/api/executors)
- [playwright](/technologies/test-tools/playwright/api/executors/playwright)
- [merge-reports](/technologies/test-tools/playwright/api/executors/merge-reports)
- [generators](/technologies/test-tools/playwright/api/generators)
- [configuration](/technologies/test-tools/playwright/api/generators/configuration)
- [init](/technologies/test-tools/playwright/api/generators/init)
+2 -2
View File
@@ -23,9 +23,9 @@ This tutorial requires a [GitHub account](https://github.com) to demonstrate the
### Step 1: Creating a new Nx Angular workspace
Let's [create your workspace](https://cloud.nx.app/create-nx-workspace/angular/github) with our Angular preset to get started quickly.
Let's create your workspace. The setup process takes about 2 minutes and will configure Angular, testing, and CI/CD automatically.
{% call-to-action variant="default" title="Create Angular Workspace" url="https://cloud.nx.app/create-nx-workspace/angular/github" /%}
{% call-to-action variant="default" title="Create Angular Workspace in 2 Minutes ⚡" url="https://cloud.nx.app/create-nx-workspace/angular/github" description="Skip the setup hassle - Get coding instantly with pre-configured CI/CD" /%}
### Step 2: Verify Your Setup
+2 -2
View File
@@ -126,9 +126,9 @@ Finally, commit and push all the changes to GitHub and proceed with finishing yo
> Nx Cloud provides self-healing CI, remote caching and many other features. [Learn more about Nx Cloud features](/ci/features).
Click the link printing in your terminal, or you can [finish setup in Nx Cloud](https://cloud.nx.app/setup/connect-workspace/github/select)
Click the link printing in your terminal, or you can connect your existing workspace to Nx Cloud:
{% call-to-action variant="simple" title="Finish Nx Cloud Setup" url="https://cloud.nx.app/setup/connect-workspace/github/select" /%}
{% call-to-action variant="gradient-alt" title="Finish Nx Cloud Setup ☁️" url="https://cloud.nx.app/setup/connect-workspace/github/select" description="Nx Cloud needs to be setup to complete the tutorial" /%}
### Verify your setup
+2 -2
View File
@@ -23,9 +23,9 @@ This tutorial requires a [GitHub account](https://github.com) to demonstrate the
### Step 1: Creating a new Nx React workspace
Let's [create your workspace](https://cloud.nx.app/create-nx-workspace/react/github) with our React preset to get started quickly.
Let's create your workspace. The setup process takes about 2 minutes and will configure React, testing, and CI/CD automatically.
{% call-to-action variant="simple" title="Create Workspace" url="https://cloud.nx.app/create-nx-workspace/react/github" /%}
{% call-to-action variant="inverted" title="Start Building React Apps 10x Faster →" url="https://cloud.nx.app/create-nx-workspace/react/github" description="Zero-config setup with caching, testing, and CI ready out of the box" /%}
### Step 2: Verify Your Setup
+3 -3
View File
@@ -21,11 +21,11 @@ What you'll learn:
This tutorial requires a [GitHub account](https://github.com) to demonstrate the full value of **Nx** - including task running, caching, and CI integration.
{% /callout %}
### Step 1: Creating a new Nx TypeScript workspace (required)
### Step 1: Creating a new Nx TypeScript workspace
Let's [create your workspace](https://cloud.nx.app/create-nx-workspace/typescript/github) with our TypeScript preset to get started quickly.
Let's create your workspace. The setup process takes about 2 minutes and will configure TypeScript, testing, and CI/CD automatically.
{% call-to-action variant="simple" title="Create TypeScript Workspace" url="https://cloud.nx.app/create-nx-workspace/typescript/github" /%}
{% call-to-action variant="gradient" title="Join 1M+ Developers Using Nx Cloud 🚀" url="https://cloud.nx.app/create-nx-workspace/typescript/github" description="Transform your TypeScript workflow - Setup takes less than 2 minutes" /%}
### Step 2: Verify Your Setup
+1 -1
View File
@@ -18,7 +18,7 @@ export const metadata: Metadata = {
'Master Nx with expert-led video courses from the core team. Boost your skills and productivity.',
images: [
{
url: 'https://nx.dev/socials/nx-media.png',
url: 'https://nx.dev/socials/nx-courses-media.png',
width: 800,
height: 421,
alt: 'Nx Video Courses',
+1 -8
View File
@@ -1,16 +1,9 @@
const path = require('path');
const siteUrl = process.env.SITE_URL || 'https://nx.dev';
/**
* @type {import('next-sitemap').IConfig}
**/
module.exports = {
siteUrl,
siteUrl: process.env.SITE_URL || 'https://nx.dev',
generateRobotsTxt: true,
exclude: [],
sourceDir: path.resolve(__dirname, '../../dist/nx-dev/nx-dev/.next'),
outDir: path.resolve(__dirname, '../../dist/nx-dev/nx-dev/public'),
robotsTxtOptions: {
additionalSitemaps: [`${siteUrl}/docs/sitemap-index.xml`],
},
};
+1 -2
View File
@@ -9,7 +9,6 @@ import '../styles/main.css';
import Link from 'next/link';
import { FrontendObservability } from '../lib/components/frontend-observability';
import GlobalScripts from '../app/global-scripts';
import { WebinarNotifier } from 'nx-dev/ui-common/src';
export default function CustomApp({
Component,
@@ -98,7 +97,7 @@ export default function CustomApp({
</Link>
<Component {...pageProps} />
{/* <LiveStreamNotifier /> */}
<WebinarNotifier />
{/* <WebinarNotifier /> */}
{/* All tracking scripts consolidated in GlobalScripts component */}
<GlobalScripts
+10 -9
View File
@@ -71,7 +71,7 @@ const docsToAstroRedirects = {
'/ci/recipes/other/cipe-affected-project-graph':
'/docs/guides/nx-cloud/cipe-affected-project-graph',
'/ci/reference': '/docs/reference',
'/ci/reference/config': '/docs/reference/nx-cloud/config',
'/ci/reference/config': '/docs/reference', // TODO: missing
'/ci/reference/nx-cloud-cli': '/docs/reference/nx-cloud-cli',
'/ci/reference/launch-templates': '/docs/reference/nx-cloud/launch-templates',
'/ci/troubleshooting': '/docs/troubleshooting', // combined index listing
@@ -79,12 +79,12 @@ const docsToAstroRedirects = {
'/docs/troubleshooting/ci-execution-failed',
'/ci/recipes/enterprise/single-tenant':
'/docs/enterprise/single-tenant/overview',
'/ci/reference/assignment-rules': '/docs/reference/nx-cloud/assignment-rules',
'/ci/reference/custom-steps': '/docs/reference/nx-cloud/custom-steps',
'/ci/reference/custom-images': '/docs/reference/nx-cloud/custom-images',
'/ci/reference/assignment-rules': '/docs/reference', // TODO: missing
'/ci/reference/custom-steps': '/docs/reference', // TODO: missing
'/ci/reference/custom-images': '/docs/reference', // TODO: missing
'/ci/reference/env-vars': '/docs/reference/environment-variables',
'/ci/reference/credits-pricing': '/docs/reference/nx-cloud/credits-pricing',
'/ci/reference/release-notes': '/docs/reference/nx-cloud/release-notes',
'/ci/reference/credits-pricing': '/docs/reference', // TODO: missing
'/ci/reference/release-notes': '/docs/reference', // TODO: missing
// ========== CONCEPTS ==========
'/concepts': '/docs/concepts',
@@ -1805,7 +1805,7 @@ const docsToAstroRedirects = {
// ================= CI ================
'/ci': '/docs/getting-started/nx-cloud',
'/ci/recipes': '/docs/guides/nx-cloud',
'/ci/recipes/improving-ttg': '/docs/guides/nx-cloud/optimize-your-ttg',
'/ci/recipes/improving-ttg': '/docs/guides/nx-cloud/setup-ci', // TODO: missing
'/ci/recipes/set-up': '/docs/guides/nx-cloud/setup-ci',
'/ci/recipes/set-up/monorepo-ci-azure': '/docs/guides/nx-cloud/setup-ci',
'/ci/recipes/set-up/monorepo-ci-circle-ci': '/docs/guides/nx-cloud/setup-ci',
@@ -1824,8 +1824,8 @@ const docsToAstroRedirects = {
'/ci/recipes/other': '/docs/guides/nx-cloud/setup-ci',
// ============= SEE-ALSO =============
'/see-also': '/docs/getting-started/intro', // missing (but it wasn't really used i.e. ~0.01% of all traffic)
'/see-also/sitemap': '/docs/getting-started/intro', // missing (but it wasn't really used i.e. ~0.01% of all traffic)
'/see-also': '/docs/getting-started/intro', // TODO: missing
'/see-also/sitemap': '/docs/getting-started/intro', // TODO: missing
// ============= SHOWCASE =============
// We removed these outdated showcase pages, but some have moved to reference (e.g. benchmarks)
@@ -1850,6 +1850,7 @@ const docsToAstroRedirects = {
'/showcase/example-repos/mfe': '/docs/getting-started/intro',
// ============ DEPRECATED ============
// TODO: 17 broken links
// "/deprecated": "/docs/deprecated",
// "/deprecated/affected-graph": "/docs/deprecated/affected-graph",
// "/deprecated/print-affected": "/docs/deprecated/print-affected",
@@ -11,7 +11,7 @@ import {
export function WebinarNotifier(): ReactElement | null {
const [isMounted, setIsMounted] = useState(false);
const [isVisible, setIsVisible] = useState<boolean>(true);
const localStorageKey = 'workshop-september-24-2025--notifier-closed';
const localStorageKey = 'workshop-august-26-2025--notifier-closed';
useEffect(() => {
setIsMounted(true);
@@ -61,17 +61,18 @@ export function WebinarNotifier(): ReactElement | null {
aria-hidden="true"
className="size-8 flex-shrink-0"
/>
<span>Join our webinar on September 24th</span>
<span>Join our webinar on August 26th</span>
</motion.h3>
<motion.div key="live-event" className="mt-4 space-y-4">
<p className="mb-2 text-sm">
Learn what modern monorepo development looks like in our September
webinar.
Learn how Nxs AI-powered self-healing CI can reduce your
development bottlenecks and free you from hours of manual PR
oversight.
</p>
<div className="flex flex-wrap items-center justify-end gap-1 sm:gap-4">
<a
title="Signup"
href="https://bit.ly/3JX0gzB"
href="https://bit.ly/4maTaFI"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center justify-center gap-2 rounded-lg bg-pink-600 px-2 py-2 text-sm font-semibold text-white transition hover:bg-pink-700 focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 active:text-black/70 md:px-4"
+1 -2
View File
@@ -1,7 +1,6 @@
import { ButtonLink, SectionHeading } from '@nx/nx-dev-ui-common';
import { type ReactElement } from 'react';
import { sendCustomEvent } from '@nx/nx-dev-feature-analytics';
import { WebinarSection } from './webinar-section';
export function Hero(): ReactElement {
return (
@@ -25,7 +24,7 @@ export function Hero(): ReactElement {
<div className="absolute inset-0">
<div className="mx-auto max-w-7xl lg:flex">
<div className="mx-auto max-w-3xl px-6 pb-24 pt-36 lg:mx-0 lg:shrink-0 lg:px-8">
<WebinarSection />
{/* <WebinarSection /> */}
<SectionHeading
id="get-speed-and-scale"
as="h1"
@@ -2,25 +2,25 @@ import React from 'react';
import { ChevronRightIcon } from '@heroicons/react/24/outline';
export const WebinarSection: React.FC = () => {
// return undefined;
return (
<p>
<a
href="https://bit.ly/3JX0gzB"
title="See live event in details"
className="group/event-link inline-flex space-x-6"
>
<span className="rounded-full bg-blue-600/10 px-3 py-1 text-sm/6 font-semibold text-blue-600 ring-1 ring-inset ring-blue-600/10 dark:bg-cyan-600/10 dark:text-cyan-600 dark:ring-cyan-600/10">
Live event
</span>
<span className="inline-flex items-center space-x-2 text-sm/6 font-medium">
<span>Webinar on September 24th</span>
<ChevronRightIcon
aria-hidden="true"
className="size-5 transform transition-all group-hover/event-link:translate-x-1"
/>
</span>
</a>
</p>
);
return undefined;
// return (
// <p>
// <a
// href="https://bit.ly/4maTaFI"
// title="See live event in details"
// className="group/event-link inline-flex space-x-6"
// >
// <span className="rounded-full bg-blue-600/10 px-3 py-1 text-sm/6 font-semibold text-blue-600 ring-1 ring-inset ring-blue-600/10 dark:bg-cyan-600/10 dark:text-cyan-600 dark:ring-cyan-600/10">
// Live event
// </span>
// <span className="inline-flex items-center space-x-2 text-sm/6 font-medium">
// <span>Webinar on August 26th</span>
// <ChevronRightIcon
// aria-hidden="true"
// className="size-5 transform transition-all group-hover/event-link:translate-x-1"
// />
// </span>
// </a>
// </p>
// );
};
@@ -47,12 +47,6 @@ const variantClasses: Record<
hoverText: 'hover:text-blue-100 dark:hover:text-sky-200 text-white',
expandBg: 'group-hover:w-full',
},
simple: {
container: 'bg-blue-600 dark:bg-blue-600',
accent: 'bg-transparent',
hoverText: 'text-white',
expandBg: '',
},
};
export type CallToActionProps = {
@@ -61,7 +55,7 @@ export type CallToActionProps = {
description?: string;
icon?: string;
size?: 'sm' | 'md' | 'lg';
variant?: 'default' | 'gradient' | 'inverted' | 'gradient-alt' | 'simple';
variant?: 'default' | 'gradient' | 'inverted' | 'gradient-alt';
};
export function CallToAction({
@@ -75,26 +69,6 @@ export function CallToAction({
const iconClasses = iconSizeClasses[size];
const colorClasses = variantClasses?.[variant] ?? variantClasses['default'];
if (variant === 'simple') {
return (
<div className="not-content not-prose mx-auto my-12 flex justify-center">
<a
href={url}
target="_blank"
rel="noreferrer"
className={classNames(
colorClasses.container,
colorClasses.hoverText,
'inline-flex items-center gap-2 rounded-md px-6 py-3 font-medium no-underline shadow-sm'
)}
>
{title}
<ChevronRightIcon className="h-5 w-5" />
</a>
</div>
);
}
return (
<div
className={classNames(
@@ -29,7 +29,7 @@ export const callToAction: Schema = {
type: 'String',
required: false,
default: 'default',
matches: ['default', 'gradient', 'inverted', 'gradient-alt', 'simple'],
matches: ['default', 'gradient', 'inverted', 'gradient-alt'],
},
size: {
// 'Size of the call to action. Defaults to "sm".',
+19 -19
View File
@@ -83,28 +83,28 @@
"@notionhq/client": "^2.2.15",
"@nuxt/kit": "^3.10.0",
"@nuxt/schema": "^3.10.0",
"@nx/angular": "21.6.1-beta.1",
"@nx/angular": "21.5.1-beta.5",
"@nx/conformance": "3.0.0",
"@nx/cypress": "21.6.1-beta.1",
"@nx/devkit": "21.6.1-beta.1",
"@nx/cypress": "21.5.1-beta.5",
"@nx/devkit": "21.5.1-beta.5",
"@nx/enterprise-cloud": "3.0.0",
"@nx/esbuild": "21.6.1-beta.1",
"@nx/eslint": "21.6.1-beta.1",
"@nx/eslint-plugin": "21.6.1-beta.1",
"@nx/gradle": "21.6.1-beta.1",
"@nx/jest": "21.6.1-beta.1",
"@nx/js": "21.6.1-beta.1",
"@nx/esbuild": "21.5.1-beta.5",
"@nx/eslint": "21.5.1-beta.5",
"@nx/eslint-plugin": "21.5.1-beta.5",
"@nx/gradle": "21.5.1-beta.5",
"@nx/jest": "21.5.1-beta.5",
"@nx/js": "21.5.1-beta.5",
"@nx/key": "3.0.0",
"@nx/next": "21.6.1-beta.1",
"@nx/playwright": "21.6.1-beta.1",
"@nx/next": "21.5.1-beta.5",
"@nx/playwright": "21.5.1-beta.5",
"@nx/powerpack-license": "3.0.0",
"@nx/react": "21.6.1-beta.1",
"@nx/rsbuild": "21.6.1-beta.1",
"@nx/rspack": "21.6.1-beta.1",
"@nx/storybook": "21.6.1-beta.1",
"@nx/vite": "21.6.1-beta.1",
"@nx/web": "21.6.1-beta.1",
"@nx/webpack": "21.6.1-beta.1",
"@nx/react": "21.5.1-beta.5",
"@nx/rsbuild": "21.5.1-beta.5",
"@nx/rspack": "21.5.1-beta.5",
"@nx/storybook": "21.5.1-beta.5",
"@nx/vite": "21.5.1-beta.5",
"@nx/web": "21.5.1-beta.5",
"@nx/webpack": "21.5.1-beta.5",
"@phenomnomnominal/tsquery": "~5.0.1",
"@playwright/test": "^1.36.1",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
@@ -263,7 +263,7 @@
"ng-packagr": "~20.2.0",
"npm-package-arg": "11.0.1",
"nuxt": "^3.10.0",
"nx": "21.6.1-beta.1",
"nx": "21.5.1-beta.5",
"nx-mcp": "^0.1.0",
"octokit": "^2.0.14",
"open": "^8.4.0",
@@ -130,14 +130,14 @@ class AddTestCiTargetsTest {
parentFile.mkdirs()
writeText(
"""
package com.example;
import org.junit.jupiter.api.Test;
abstract class AbstractTest {
@Test
void testMethod() {}
}
"""
package com.example;
import org.junit.jupiter.api.Test;
abstract class AbstractTest {
@Test
void testMethod() {}
}
"""
.trimIndent())
}
@@ -146,14 +146,14 @@ class AddTestCiTargetsTest {
parentFile.mkdirs()
writeText(
"""
package com.example;
import org.junit.jupiter.api.Test;
class ConcreteTest {
@Test
void testMethod() {}
}
"""
package com.example;
import org.junit.jupiter.api.Test;
class ConcreteTest {
@Test
void testMethod() {}
}
"""
.trimIndent())
}
@@ -33,14 +33,14 @@ class CompileTestCiTargetsTest {
parentFile.mkdirs()
writeText(
"""
package com.example
import org.junit.jupiter.api.Test
class UserServiceTest {
@Test
fun testService() {}
}
"""
package com.example
import org.junit.jupiter.api.Test
class UserServiceTest {
@Test
fun testService() {}
}
"""
.trimIndent())
}
@@ -49,14 +49,14 @@ class CompileTestCiTargetsTest {
parentFile.mkdirs()
writeText(
"""
package com.example
import org.junit.jupiter.api.Test
class UserRepositoryTest {
@Test
fun testRepository() {}
}
"""
package com.example
import org.junit.jupiter.api.Test
class UserRepositoryTest {
@Test
fun testRepository() {}
}
"""
.trimIndent())
}
+2
View File
@@ -21,6 +21,8 @@ pub enum Action {
UnpinTask(String, usize),
UnpinAllTasks,
SortTasks,
NextPage,
PreviousPage,
NextTask,
PreviousTask,
SetSpacebarMode(bool),
+22 -2
View File
@@ -684,6 +684,14 @@ impl App {
self.dispatch_action(Action::PreviousTask);
let _ = self.debounce_pty_resize();
}
KeyCode::Left => {
self.dispatch_action(Action::PreviousPage);
let _ = self.debounce_pty_resize();
}
KeyCode::Right => {
self.dispatch_action(Action::NextPage);
let _ = self.debounce_pty_resize();
}
KeyCode::Esc => {
if matches!(self.focus, Focus::HelpPopup) {
if let Some(help_popup) =
@@ -740,6 +748,18 @@ impl App {
// No need to debounce
}
'0' => self.clear_all_panes(),
'h' => {
self.dispatch_action(
Action::PreviousPage,
);
let _ = self.debounce_pty_resize();
}
'l' => {
self.dispatch_action(
Action::NextPage,
);
let _ = self.debounce_pty_resize();
}
'b' => self.toggle_task_list(),
'm' => {
if let Some(area) = self.frame_area
@@ -809,7 +829,7 @@ impl App {
match mouse.kind {
MouseEventKind::ScrollUp => {
if matches!(self.focus, Focus::TaskList) {
self.dispatch_action(Action::ScrollUp);
self.dispatch_action(Action::PreviousTask);
} else {
self.handle_key_event(KeyEvent::new(
KeyCode::Up,
@@ -820,7 +840,7 @@ impl App {
}
MouseEventKind::ScrollDown => {
if matches!(self.focus, Focus::TaskList) {
self.dispatch_action(Action::ScrollDown);
self.dispatch_action(Action::NextTask);
} else {
self.handle_key_event(KeyEvent::new(
KeyCode::Down,
+1
View File
@@ -14,6 +14,7 @@ pub mod dependency_view;
pub mod help_popup;
pub mod help_text;
pub mod layout_manager;
pub mod pagination;
pub mod task_selection_manager;
pub mod tasks_list;
pub mod terminal_pane;
@@ -128,6 +128,8 @@ impl HelpPopup {
("↓ or j", "Navigate/scroll task output down"),
("<ctrl>+u", "Scroll task output up"),
("<ctrl>+d", "Scroll task output down"),
("← or h", "Navigate left"),
("→ or l", "Navigate right"),
("", ""),
// Task List Controls
("/", "Filter tasks based on search term"),
@@ -0,0 +1,83 @@
use crate::native::tui::theme::THEME;
use ratatui::{
Frame,
layout::Rect,
style::{Modifier, Style},
text::{Line, Span},
widgets::Paragraph,
};
pub struct Pagination {
current_page: usize,
total_pages: usize,
}
impl Pagination {
pub fn new(current_page: usize, total_pages: usize) -> Self {
Self {
current_page,
total_pages,
}
}
/// Renders the pagination at the given location with the specified focus state.
pub fn render(&self, f: &mut Frame<'_>, area: Rect, is_dimmed: bool) {
// Add a safety check to prevent rendering outside buffer bounds (this can happen if the user resizes the window a lot before it stabilizes it seems)
if area.height == 0
|| area.width == 0
|| area.x >= f.area().width
|| area.y >= f.area().height
{
return; // Area is out of bounds, don't try to render
}
// Ensure area is entirely within frame bounds
let safe_area = Rect {
x: area.x,
y: area.y,
width: area.width.min(f.area().width.saturating_sub(area.x)),
height: area.height.min(f.area().height.saturating_sub(area.y)),
};
let base_style = if is_dimmed {
Style::default().add_modifier(Modifier::DIM)
} else {
Style::default()
};
let mut spans = vec![];
// Ensure we have at least 1 page
let total_pages = self.total_pages.max(1);
let current_page = self.current_page.min(total_pages - 1);
// Left arrow - dim if we're on the first page
let left_arrow = if current_page == 0 {
Span::styled("", base_style.fg(THEME.info).add_modifier(Modifier::DIM))
} else {
Span::styled("", base_style.fg(THEME.info))
};
spans.push(left_arrow);
// Page numbers
spans.push(Span::raw(" "));
spans.push(Span::styled(
format!("{}/{}", current_page + 1, total_pages),
base_style.fg(THEME.secondary_fg),
));
spans.push(Span::raw(" "));
// Right arrow - dim if we're on the last page
let right_arrow = if current_page >= total_pages.saturating_sub(1) {
Span::styled("", base_style.fg(THEME.info).add_modifier(Modifier::DIM))
} else {
Span::styled("", base_style.fg(THEME.info))
};
spans.push(right_arrow);
let pagination_line = Line::from(spans);
let pagination = Paragraph::new(pagination_line);
f.render_widget(pagination, safe_area);
}
}
@@ -4,7 +4,7 @@ expression: terminal.backend()
---
" "
" NX Running Test Tasks... Cache Duration"
" "
" "
" "
" "
" "
@@ -16,4 +16,4 @@ expression: terminal.backend()
" Filter: app1 "
" -> 3 tasks filtered out. Press / to persist, <esc> to clear "
" "
" quit: q help: ? navigate: ↑ ↓ filter: / pin output: 1 or 2 show output: <enter>"
" ← 1/1 → quit: q help: ? navigate: ↑ ↓ filter: / pin output: 1 or 2 show output: <enter>"
@@ -16,4 +16,4 @@ expression: terminal.backend()
" "
" "
" "
" quit: q help: ? navigate: ↑ ↓ filter: / pin output: 1 or 2 show output: <enter>"
" ← 1/1 → quit: q help: ? navigate: ↑ ↓ filter: / pin output: 1 or 2 show output: <enter>"
@@ -1,14 +0,0 @@
---
source: packages/nx/src/native/tui/components/tasks_list.rs
expression: terminal.backend()
---
" "
" NX Running Test Tasks... Cache Duration "
" ↑"
" █"
"> · task1 ... ... █"
" · task10 ... ... ║"
" · task2 ... ... ║"
" · task3 ... ... ║"
" ↓"
" quit: q help: ?"
@@ -1,14 +0,0 @@
---
source: packages/nx/src/native/tui/components/tasks_list.rs
expression: terminal.backend()
---
" "
" NX Running Test Tasks... Cache Duration "
" │ ↑"
" │· Waiting for task... █"
" │· Waiting for task... ║"
" └ ║"
"> · task1 ... ... ║"
" · task10 ... ... ║"
" ↓"
" quit: q help: ?"
@@ -16,4 +16,4 @@ expression: terminal.backend()
" "
" "
" "
" quit: q help: ?"
" ← 1/1 → quit: q help: ?"
@@ -16,4 +16,4 @@ expression: terminal.backend()
" "
" "
" "
"View logs and run details at https://nx.app/runs/KnGk4A47qk quit: q help: ?"
" ← 1/1 → View logs and run details at https://nx.app/runs/KnGk4A47qk quit: q help: ?"
@@ -1,19 +0,0 @@
---
source: packages/nx/src/native/tui/components/tasks_list.rs
expression: terminal.backend()
---
" "
" NX Completed Test Tasks Cache Duration"
" "
" ✔ task1 - ..."
"> ✔ task2 - ..."
" ✔ task3 - ..."
" "
" "
" "
" "
" "
" "
" "
" "
"https://nx.app/runs/KnGk4A47qk quit: q help: ?"
@@ -1,20 +1,19 @@
---
source: packages/nx/src/native/tui/components/tasks_list.rs
assertion_line: 2748
expression: terminal.backend()
---
" "
" NX Completed Test Tasks Cache Duration"
" "
" ✔ task1 - ..."
"> ✔ task2 - ..."
" ✔ task3 - ..."
" "
" "
" "
" "
" "
" "
" https://nx.app/runs/KnGk4A47qk "
" "
" quit: q help: ?"
" "
" NX Completed Test Tasks Cache Duration"
" "
" ✔ task1 - ..."
"> ✔ task2 - ..."
" ✔ task3 - ..."
" "
" "
" "
" "
" "
" "
" https://nx.app/runs/KnGk4A47qk "
" "
" ← 1/1 → quit: q help: ?"
@@ -16,4 +16,4 @@ expression: terminal.backend()
" "
" "
" "
"This is some warning from Nx Cloud quit: q help: ?"
" ← 1/1 → This is some warning from Nx Cloud quit: q help: ?"
@@ -16,4 +16,4 @@ expression: terminal.backend()
" "
" "
" "
"This is some warning from Nx Cloud quit: q help: ? navigate: ↑ ↓ filter: / pin output: 1 or 2 show output: <enter>"
" ← 1/1 → This is some warning from Nx Cloud quit: q help: ? navigate: ↑ ↓ filter: / pin output: 1 or 2 show output: <enter>"
@@ -16,4 +16,4 @@ expression: terminal.backend()
" "
" "
" "
" quit: q help: ? navigate: ↑ ↓ filter: / pin output: 1 or 2 show output: <enter>"
" ← 1/1 → quit: q help: ? navigate: ↑ ↓ filter: / pin output: 1 or 2 show output: <enter>"
@@ -16,4 +16,4 @@ expression: terminal.backend()
" "
" "
" "
" quit: q help: ? navigate: ↑ ↓ filter: / pin output: 1 or 2 show output: <enter>"
" ← 1/1 → quit: q help: ? navigate: ↑ ↓ filter: / pin output: 1 or 2 show output: <enter>"
@@ -1,10 +0,0 @@
---
source: packages/nx/src/native/tui/components/tasks_list.rs
expression: terminal.backend()
---
" "
" NX Running Deep Scroll Duration "
" ↑"
"> · task1 ... █"
" ↓"
" quit: q help: ?"
@@ -16,4 +16,4 @@ expression: terminal.backend()
" "
" "
" "
" quit: q help: ? navigate: ↑ ↓ filter: / pin output: 1 or 2 show output: <enter>"
" ← 1/1 → quit: q help: ? navigate: ↑ ↓ filter: / pin output: 1 or 2 show output: <enter>"

Some files were not shown because too many files have changed in this diff Show More