Skip to content

Releases: gooddata/gooddata-goodchanges

Release v0.25.8

Choose a tag to compare

@github-actions github-actions released this 20 Aug 04:44
de5fe1b

Docker Image

The Docker image for this release has been published to DockerHub:

Repository: gooddata/gooddata-goodchanges

Tags:

  • gooddata/gooddata-goodchanges:0.25.8
  • gooddata/gooddata-goodchanges:latest

Pull Commands

# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.8

# Pull latest
docker pull gooddata/gooddata-goodchanges:latest

Run Command

docker run --rm gooddata/gooddata-goodchanges:0.25.8 [command]

Standalone Binaries

Download the binary for your platform from the assets below.

Platform Architecture Asset
Linux x86_64 goodchanges-linux-amd64.tar.gz
Linux ARM64 goodchanges-linux-arm64.tar.gz
macOS Intel goodchanges-darwin-amd64.tar.gz
macOS Apple Silicon goodchanges-darwin-arm64.tar.gz
Windows x86_64 goodchanges-windows-amd64.zip
Windows ARM64 goodchanges-windows-arm64.zip
All platforms
  • goodchanges-darwin-amd64.tar.gz
  • goodchanges-darwin-amd64.tar.gz.sha256
  • goodchanges-darwin-arm64.tar.gz
  • goodchanges-darwin-arm64.tar.gz.sha256
  • goodchanges-linux-amd64.tar.gz
  • goodchanges-linux-amd64.tar.gz.sha256
  • goodchanges-linux-arm64.tar.gz
  • goodchanges-linux-arm64.tar.gz.sha256
  • goodchanges-windows-amd64.zip
  • goodchanges-windows-amd64.zip.sha256
  • goodchanges-windows-arm64.zip
  • goodchanges-windows-arm64.zip.sha256

Install (Linux/macOS)

# Example: download and install linux/amd64
curl -sL https://github.com/gooddata/gooddata-goodchanges/releases/download/v0.25.8/goodchanges-linux-amd64.tar.gz | tar xz
chmod +x goodchanges-linux-amd64
sudo mv goodchanges-linux-amd64 /usr/local/bin/goodchanges

Changelog

[0.25.8] - 2026-08-20

Fixed

  • Import-time side-effect taint no longer propagates through type-only import/re-export edges. import type { T } from "./x" and export type { T } from "./x" are erased at compile time and never load ./x at runtime, but the dependency graph didn't distinguish them from runtime edges — so a barrel whose only link to a side-effectful module was a type-only re-export was wholesale-tainted (all runtime symbols + the __side-effect__ sentinel) and kept cascading to its own consumers, flagging targets for changes their code can never execute. Import edges now carry the statement's type-only-ness (tracked on tsparse.Import since 0.25.3) and synthetic re-export edges carry the export's; the side-effect transitivity blocks in both propagation paths fire only when at least one runtime edge connects the importer to the side-effectful module. A runtime re-export correctly upgrades a previously-recorded type-only edge to the same source (the deduped edge is flipped, so export type { T } from "./x" followed by export { f } from "./x" still counts as a runtime connection). Ordinary named-symbol taint through type-only edges is unchanged (relevant for INCLUDE_TYPES); only the side-effect wildcard/sentinel flow is gated.

[0.25.7] - 2026-08-11

Fixed

  • Spec files that import a tainted symbol directly from an upstream workspace package are now detected. The fine-grained path taints files via findTaintedSymbolsByUsage, which only marks declared symbols — and spec files typically declare none (top-level test()/describe() calls aren't declarations). The dedicated leaf-file pass that exists for exactly this shape only follows local import edges, so a spec importing e.g. deleteLLMProvider from "gdc-e2e-utils" was never tainted: a change to that function flagged only packages whose specs reached it indirectly through a local helper (gdc-analytical-designer-e2e), while packages whose specs import it directly (gdc-dashboards-e2e, gdc-catalog-e2e, gdc-host-application-e2e) were silently missed. The upstream-workspace seeding now mirrors the leaf-file pass: when a file imports a tainted name and no declared symbol carries the usage, the file is marked wholesale (*); a bare upstream import in a declaration-less file gets the same marker. Packages whose specs reference a same-named local wrapper (e.g. gdc-ldm-modeler-e2e's own helpers/api/ai.js) are correctly not flagged, and files whose usage does live in declared symbols keep the precise symbol-level taint.

[0.25.6] - 2026-08-11

Fixed

  • Import-time side effects are now detected even when the same diff also changes symbols. The hasSideEffectStmtChanges / bareImportsChanged checks ran only as a fallback when the symbol-level AST diff came up empty (len(affected) == 0), so a single commit that both edited an exported symbol and added/removed a top-level side-effect statement (console.log(...)) or a bare import "x" produced only the symbol taint — the "*" wildcard and __side-effect__ sentinel were dropped, cutting off consumers of the file's other exports and the transitive barrel propagation added in 0.25.5. The side-effect checks now run independently of symbol-level results and append "*" + __side-effect__ (plus all non-type-only symbols) whenever either fires. No over-taint is reintroduced: the checks compare only top-level side-effect statement text and the bare-import set, so a pure declaration edit still doesn't trigger them, and comment / formatting / type-only / import-reordering changes still taint nothing.

[0.25.5] - 2026-08-10

Fixed

  • Import-time side-effect taint now propagates transitively through import and re-export edges. A file whose change ran at import time (a changed top-level statement like console.log(...), or a bare import "x") tainted its direct importers, but the "runs at import time" quality was lost after one hop: a barrel that re-exports the side-effectful module (export { … } from "./api/ai/ai.js" in an entrypoint index.ts) only picked up the re-exported symbols, so consumers importing the barrel for other symbols were missed — e.g. a console.log added to e2e-utils/src/api/ai/ai.ts flagged only the 5 consumers of the ai exports instead of all 8 e2e-utils consumers (importing the barrel loads ai.ts and executes the statement regardless of which symbol is used). Such changes now carry a __side-effect__ sentinel that flows through every import/re-export edge, marking each file it reaches as wholly tainted and itself side-effectful, so barrels become side-effectful and all their consumers are flagged — the same result as adding the statement to the entrypoint directly. This is deliberately assume-the-worst; a TODO in the propagation notes the follow-up to refine it using each package's package.json "sideEffects" field (a side-effect-free module is tree-shaken and should not propagate).

[0.25.4] - 2026-08-07

Fixed

  • Intra-file taint propagation now matches symbol names as whole identifiers instead of raw substrings. The dependency/usage checks used strings.Contains(body, name), so a symbol whose name is a substring of another was falsely linked — e.g. removing the unused chooseAction tainted the surviving, used chooseActionByIndex (its body "contains" the string chooseAction), which then propagated to a spec and flagged gdc-dashboards-e2e for a dead-code deletion. All six propagation sites (astdiff.go intra-file graph, plus the seed/importer/usage propagation in analyzer.go) now require the name to appear flanked by non-identifier characters ([A-Za-z0-9_$]) via a shared containsIdentifier helper; non-identifier tokens like the * wildcard keep the substring behaviour. Strictly more precise (it only drops matches that were substrings inside a larger identifier — never a real usage), so no false negatives.

[0.25.3] - 2026-08-07

Fixed

  • Replaced the fine-grained path's whole-file * fallback with import-time-aware detection, so an empty symbol diff no longer floods every importer. Previously, when a changed file produced no symbol-level diff, FindAffectedFiles tainted the entire file with * — so a comment-only edit, a formatting change, a type-only change, or reordering imports in a widely-imported helper flagged all its consumers. That blanket is removed; a change now wildcards a file only when something that actually runs at import time changed:
    • a top-level side-effect statement (console.log(...), describe(...), test(...), etc.) — already detected via hasSideEffectStmtChanges;
    • a bare side-effect import (import "./x") added, removed, or re-pointed.
  • Named-import re-pointing is now handled precisely instead of via the blanket. When a binding keeps its name but resolves to a different module/export (import { x } from "./a" → "./b", or { a as x } → { b as x }), its usages don't change textually and the symbol diff missed them; those usages are now tainted directly. Reordering imports and re-pointing a type-only import (import type, when includeTypes is off) correctly taint nothing. Import statement type-only-ness is now tracked on tsparse.Import.

Together these keep genuine import-time changes flagged while eliminating the large false-positive class where a comment or dead-code edit to a shared file re-ran every dependent target.

  • Removing an unused export no longer floods every importer with taint. A deleted symbol was logged but never recorded as a change, so the per-file AST diff returned no affected symbols — and in the fine-grained path (FindAffectedFiles) an empty diff falls through to tainting the whole file with *. Deleting one unused export from a widely-imported helper (e.g. ERROR_MESSAGE from gdc-ldm-modeler-e2e's playwright/helpers/selectors.ts) therefore tainted every file that imported it, flagging all ~48 dependent specs. Deleted symbols are now recorded as changed and propagate by name, so a removed export taints exactly the files that imported that symbol: an unused one taints nobody, wh...
Read more

Release v0.25.7

Choose a tag to compare

@github-actions github-actions released this 11 Aug 16:11
d96618f

Docker Image

The Docker image for this release has been published to DockerHub:

Repository: gooddata/gooddata-goodchanges

Tags:

  • gooddata/gooddata-goodchanges:0.25.7
  • gooddata/gooddata-goodchanges:latest

Pull Commands

# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.7

# Pull latest
docker pull gooddata/gooddata-goodchanges:latest

Run Command

docker run --rm gooddata/gooddata-goodchanges:0.25.7 [command]

Standalone Binaries

Download the binary for your platform from the assets below.

Platform Architecture Asset
Linux x86_64 goodchanges-linux-amd64.tar.gz
Linux ARM64 goodchanges-linux-arm64.tar.gz
macOS Intel goodchanges-darwin-amd64.tar.gz
macOS Apple Silicon goodchanges-darwin-arm64.tar.gz
Windows x86_64 goodchanges-windows-amd64.zip
Windows ARM64 goodchanges-windows-arm64.zip
All platforms
  • goodchanges-darwin-amd64.tar.gz
  • goodchanges-darwin-amd64.tar.gz.sha256
  • goodchanges-darwin-arm64.tar.gz
  • goodchanges-darwin-arm64.tar.gz.sha256
  • goodchanges-linux-amd64.tar.gz
  • goodchanges-linux-amd64.tar.gz.sha256
  • goodchanges-linux-arm64.tar.gz
  • goodchanges-linux-arm64.tar.gz.sha256
  • goodchanges-windows-amd64.zip
  • goodchanges-windows-amd64.zip.sha256
  • goodchanges-windows-arm64.zip
  • goodchanges-windows-arm64.zip.sha256

Install (Linux/macOS)

# Example: download and install linux/amd64
curl -sL https://github.com/gooddata/gooddata-goodchanges/releases/download/v0.25.7/goodchanges-linux-amd64.tar.gz | tar xz
chmod +x goodchanges-linux-amd64
sudo mv goodchanges-linux-amd64 /usr/local/bin/goodchanges

Changelog

[0.25.7] - 2026-08-11

Fixed

  • Spec files that import a tainted symbol directly from an upstream workspace package are now detected. The fine-grained path taints files via findTaintedSymbolsByUsage, which only marks declared symbols — and spec files typically declare none (top-level test()/describe() calls aren't declarations). The dedicated leaf-file pass that exists for exactly this shape only follows local import edges, so a spec importing e.g. deleteLLMProvider from "gdc-e2e-utils" was never tainted: a change to that function flagged only packages whose specs reached it indirectly through a local helper (gdc-analytical-designer-e2e), while packages whose specs import it directly (gdc-dashboards-e2e, gdc-catalog-e2e, gdc-host-application-e2e) were silently missed. The upstream-workspace seeding now mirrors the leaf-file pass: when a file imports a tainted name and no declared symbol carries the usage, the file is marked wholesale (*); a bare upstream import in a declaration-less file gets the same marker. Packages whose specs reference a same-named local wrapper (e.g. gdc-ldm-modeler-e2e's own helpers/api/ai.js) are correctly not flagged, and files whose usage does live in declared symbols keep the precise symbol-level taint.

[0.25.6] - 2026-08-11

Fixed

  • Import-time side effects are now detected even when the same diff also changes symbols. The hasSideEffectStmtChanges / bareImportsChanged checks ran only as a fallback when the symbol-level AST diff came up empty (len(affected) == 0), so a single commit that both edited an exported symbol and added/removed a top-level side-effect statement (console.log(...)) or a bare import "x" produced only the symbol taint — the "*" wildcard and __side-effect__ sentinel were dropped, cutting off consumers of the file's other exports and the transitive barrel propagation added in 0.25.5. The side-effect checks now run independently of symbol-level results and append "*" + __side-effect__ (plus all non-type-only symbols) whenever either fires. No over-taint is reintroduced: the checks compare only top-level side-effect statement text and the bare-import set, so a pure declaration edit still doesn't trigger them, and comment / formatting / type-only / import-reordering changes still taint nothing.

[0.25.5] - 2026-08-10

Fixed

  • Import-time side-effect taint now propagates transitively through import and re-export edges. A file whose change ran at import time (a changed top-level statement like console.log(...), or a bare import "x") tainted its direct importers, but the "runs at import time" quality was lost after one hop: a barrel that re-exports the side-effectful module (export { … } from "./api/ai/ai.js" in an entrypoint index.ts) only picked up the re-exported symbols, so consumers importing the barrel for other symbols were missed — e.g. a console.log added to e2e-utils/src/api/ai/ai.ts flagged only the 5 consumers of the ai exports instead of all 8 e2e-utils consumers (importing the barrel loads ai.ts and executes the statement regardless of which symbol is used). Such changes now carry a __side-effect__ sentinel that flows through every import/re-export edge, marking each file it reaches as wholly tainted and itself side-effectful, so barrels become side-effectful and all their consumers are flagged — the same result as adding the statement to the entrypoint directly. This is deliberately assume-the-worst; a TODO in the propagation notes the follow-up to refine it using each package's package.json "sideEffects" field (a side-effect-free module is tree-shaken and should not propagate).

[0.25.4] - 2026-08-07

Fixed

  • Intra-file taint propagation now matches symbol names as whole identifiers instead of raw substrings. The dependency/usage checks used strings.Contains(body, name), so a symbol whose name is a substring of another was falsely linked — e.g. removing the unused chooseAction tainted the surviving, used chooseActionByIndex (its body "contains" the string chooseAction), which then propagated to a spec and flagged gdc-dashboards-e2e for a dead-code deletion. All six propagation sites (astdiff.go intra-file graph, plus the seed/importer/usage propagation in analyzer.go) now require the name to appear flanked by non-identifier characters ([A-Za-z0-9_$]) via a shared containsIdentifier helper; non-identifier tokens like the * wildcard keep the substring behaviour. Strictly more precise (it only drops matches that were substrings inside a larger identifier — never a real usage), so no false negatives.

[0.25.3] - 2026-08-07

Fixed

  • Replaced the fine-grained path's whole-file * fallback with import-time-aware detection, so an empty symbol diff no longer floods every importer. Previously, when a changed file produced no symbol-level diff, FindAffectedFiles tainted the entire file with * — so a comment-only edit, a formatting change, a type-only change, or reordering imports in a widely-imported helper flagged all its consumers. That blanket is removed; a change now wildcards a file only when something that actually runs at import time changed:
    • a top-level side-effect statement (console.log(...), describe(...), test(...), etc.) — already detected via hasSideEffectStmtChanges;
    • a bare side-effect import (import "./x") added, removed, or re-pointed.
  • Named-import re-pointing is now handled precisely instead of via the blanket. When a binding keeps its name but resolves to a different module/export (import { x } from "./a" → "./b", or { a as x } → { b as x }), its usages don't change textually and the symbol diff missed them; those usages are now tainted directly. Reordering imports and re-pointing a type-only import (import type, when includeTypes is off) correctly taint nothing. Import statement type-only-ness is now tracked on tsparse.Import.

Together these keep genuine import-time changes flagged while eliminating the large false-positive class where a comment or dead-code edit to a shared file re-ran every dependent target.

  • Removing an unused export no longer floods every importer with taint. A deleted symbol was logged but never recorded as a change, so the per-file AST diff returned no affected symbols — and in the fine-grained path (FindAffectedFiles) an empty diff falls through to tainting the whole file with *. Deleting one unused export from a widely-imported helper (e.g. ERROR_MESSAGE from gdc-ldm-modeler-e2e's playwright/helpers/selectors.ts) therefore tainted every file that imported it, flagging all ~48 dependent specs. Deleted symbols are now recorded as changed and propagate by name, so a removed export taints exactly the files that imported that symbol: an unused one taints nobody, while a removed used export still flags its importers (no false negative). The deleted names are appended after intra-file propagation (which walks only surviving symbols) and before the whole-file side-effect fallback, so a deletion-only change is carried precisely instead of being widened.

[0.25.1] - 2026-08-07

Fixed

  • Destructuring default initializers are now included in a binding's taint span. A binding's default (const { a = compute() } = obj, const [a = fallbackVal] = arr) is a real second dependency — it supplies the value when the destructured slot is undefined — but it lives on the pattern (LHS), disjoint from the mapped source (RHS), so the element-wise span recorded in 0.25.0 covered only the source and excluded the default. A symbol used only inside such a default therefore escaped findTaintedSymbolsByUsage — a false negative. The binding's span is now widened to cover the default expression as well as its mapped source. (Narrow in scope: only bites when a tainted symbol appears solely in a binding default and nowhere else in the file, but false negatives are always worth closing.)

[0.25.0] - 2026-08-03

Changed

  • Destructured bindings are now attributed element-wise when the initializer is an array or object literal,...
Read more

Release v0.25.6

Choose a tag to compare

@github-actions github-actions released this 11 Aug 02:30
60a11ad

Docker Image

The Docker image for this release has been published to DockerHub:

Repository: gooddata/gooddata-goodchanges

Tags:

  • gooddata/gooddata-goodchanges:0.25.6
  • gooddata/gooddata-goodchanges:latest

Pull Commands

# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.6

# Pull latest
docker pull gooddata/gooddata-goodchanges:latest

Run Command

docker run --rm gooddata/gooddata-goodchanges:0.25.6 [command]

Standalone Binaries

Download the binary for your platform from the assets below.

Platform Architecture Asset
Linux x86_64 goodchanges-linux-amd64.tar.gz
Linux ARM64 goodchanges-linux-arm64.tar.gz
macOS Intel goodchanges-darwin-amd64.tar.gz
macOS Apple Silicon goodchanges-darwin-arm64.tar.gz
Windows x86_64 goodchanges-windows-amd64.zip
Windows ARM64 goodchanges-windows-arm64.zip
All platforms
  • goodchanges-darwin-amd64.tar.gz
  • goodchanges-darwin-amd64.tar.gz.sha256
  • goodchanges-darwin-arm64.tar.gz
  • goodchanges-darwin-arm64.tar.gz.sha256
  • goodchanges-linux-amd64.tar.gz
  • goodchanges-linux-amd64.tar.gz.sha256
  • goodchanges-linux-arm64.tar.gz
  • goodchanges-linux-arm64.tar.gz.sha256
  • goodchanges-windows-amd64.zip
  • goodchanges-windows-amd64.zip.sha256
  • goodchanges-windows-arm64.zip
  • goodchanges-windows-arm64.zip.sha256

Install (Linux/macOS)

# Example: download and install linux/amd64
curl -sL https://github.com/gooddata/gooddata-goodchanges/releases/download/v0.25.6/goodchanges-linux-amd64.tar.gz | tar xz
chmod +x goodchanges-linux-amd64
sudo mv goodchanges-linux-amd64 /usr/local/bin/goodchanges

Changelog

[0.25.6] - 2026-08-11

Fixed

  • Import-time side effects are now detected even when the same diff also changes symbols. The hasSideEffectStmtChanges / bareImportsChanged checks ran only as a fallback when the symbol-level AST diff came up empty (len(affected) == 0), so a single commit that both edited an exported symbol and added/removed a top-level side-effect statement (console.log(...)) or a bare import "x" produced only the symbol taint — the "*" wildcard and __side-effect__ sentinel were dropped, cutting off consumers of the file's other exports and the transitive barrel propagation added in 0.25.5. The side-effect checks now run independently of symbol-level results and append "*" + __side-effect__ (plus all non-type-only symbols) whenever either fires. No over-taint is reintroduced: the checks compare only top-level side-effect statement text and the bare-import set, so a pure declaration edit still doesn't trigger them, and comment / formatting / type-only / import-reordering changes still taint nothing.

[0.25.5] - 2026-08-10

Fixed

  • Import-time side-effect taint now propagates transitively through import and re-export edges. A file whose change ran at import time (a changed top-level statement like console.log(...), or a bare import "x") tainted its direct importers, but the "runs at import time" quality was lost after one hop: a barrel that re-exports the side-effectful module (export { … } from "./api/ai/ai.js" in an entrypoint index.ts) only picked up the re-exported symbols, so consumers importing the barrel for other symbols were missed — e.g. a console.log added to e2e-utils/src/api/ai/ai.ts flagged only the 5 consumers of the ai exports instead of all 8 e2e-utils consumers (importing the barrel loads ai.ts and executes the statement regardless of which symbol is used). Such changes now carry a __side-effect__ sentinel that flows through every import/re-export edge, marking each file it reaches as wholly tainted and itself side-effectful, so barrels become side-effectful and all their consumers are flagged — the same result as adding the statement to the entrypoint directly. This is deliberately assume-the-worst; a TODO in the propagation notes the follow-up to refine it using each package's package.json "sideEffects" field (a side-effect-free module is tree-shaken and should not propagate).

[0.25.4] - 2026-08-07

Fixed

  • Intra-file taint propagation now matches symbol names as whole identifiers instead of raw substrings. The dependency/usage checks used strings.Contains(body, name), so a symbol whose name is a substring of another was falsely linked — e.g. removing the unused chooseAction tainted the surviving, used chooseActionByIndex (its body "contains" the string chooseAction), which then propagated to a spec and flagged gdc-dashboards-e2e for a dead-code deletion. All six propagation sites (astdiff.go intra-file graph, plus the seed/importer/usage propagation in analyzer.go) now require the name to appear flanked by non-identifier characters ([A-Za-z0-9_$]) via a shared containsIdentifier helper; non-identifier tokens like the * wildcard keep the substring behaviour. Strictly more precise (it only drops matches that were substrings inside a larger identifier — never a real usage), so no false negatives.

[0.25.3] - 2026-08-07

Fixed

  • Replaced the fine-grained path's whole-file * fallback with import-time-aware detection, so an empty symbol diff no longer floods every importer. Previously, when a changed file produced no symbol-level diff, FindAffectedFiles tainted the entire file with * — so a comment-only edit, a formatting change, a type-only change, or reordering imports in a widely-imported helper flagged all its consumers. That blanket is removed; a change now wildcards a file only when something that actually runs at import time changed:
    • a top-level side-effect statement (console.log(...), describe(...), test(...), etc.) — already detected via hasSideEffectStmtChanges;
    • a bare side-effect import (import "./x") added, removed, or re-pointed.
  • Named-import re-pointing is now handled precisely instead of via the blanket. When a binding keeps its name but resolves to a different module/export (import { x } from "./a" → "./b", or { a as x } → { b as x }), its usages don't change textually and the symbol diff missed them; those usages are now tainted directly. Reordering imports and re-pointing a type-only import (import type, when includeTypes is off) correctly taint nothing. Import statement type-only-ness is now tracked on tsparse.Import.

Together these keep genuine import-time changes flagged while eliminating the large false-positive class where a comment or dead-code edit to a shared file re-ran every dependent target.

  • Removing an unused export no longer floods every importer with taint. A deleted symbol was logged but never recorded as a change, so the per-file AST diff returned no affected symbols — and in the fine-grained path (FindAffectedFiles) an empty diff falls through to tainting the whole file with *. Deleting one unused export from a widely-imported helper (e.g. ERROR_MESSAGE from gdc-ldm-modeler-e2e's playwright/helpers/selectors.ts) therefore tainted every file that imported it, flagging all ~48 dependent specs. Deleted symbols are now recorded as changed and propagate by name, so a removed export taints exactly the files that imported that symbol: an unused one taints nobody, while a removed used export still flags its importers (no false negative). The deleted names are appended after intra-file propagation (which walks only surviving symbols) and before the whole-file side-effect fallback, so a deletion-only change is carried precisely instead of being widened.

[0.25.1] - 2026-08-07

Fixed

  • Destructuring default initializers are now included in a binding's taint span. A binding's default (const { a = compute() } = obj, const [a = fallbackVal] = arr) is a real second dependency — it supplies the value when the destructured slot is undefined — but it lives on the pattern (LHS), disjoint from the mapped source (RHS), so the element-wise span recorded in 0.25.0 covered only the source and excluded the default. A symbol used only inside such a default therefore escaped findTaintedSymbolsByUsage — a false negative. The binding's span is now widened to cover the default expression as well as its mapped source. (Narrow in scope: only bites when a tainted symbol appears solely in a binding default and nowhere else in the file, but false negatives are always worth closing.)

[0.25.0] - 2026-08-03

Changed

  • Destructured bindings are now attributed element-wise when the initializer is an array or object literal, refining the whole-initializer attribution added in 0.24.13. Previously every binding in const [a, b] = [tainted(), safe()] (or const { a, b } = { a: tainted(), b: safe() }) shared the entire initializer's span, so a change touching only one element tainted all the bindings — a false positive. Each binding is now mapped to its corresponding element (by index for array literals, by key for object literals, following nested patterns), and only falls back to the shared initializer span when mapping can't be done statically: a non-literal initializer (a call/identifier/member access — e.g. createStore(), where all bindings genuinely share the dependency), rest bindings (...rest), spreads, computed keys, or out-of-range indices. Net effect: a depends on tainted() and b on safe(), so a change to safe() no longer flags consumers of a.

[0.24.13] - 2026-08-01

Fixed

  • Destructuring variable declarations (const { a, b } = expr / const [a, b] = expr) are now decomposed into their individual bindings for both symbol and export extraction. Previously getDeclName only handled plain identifiers and returned "" for binding patterns, so destructured names were registered as neither symbols nor exports — they simply didn't exist in the file model. That sil...
Read more

Release v0.25.5

Choose a tag to compare

@github-actions github-actions released this 10 Aug 10:32
f4f4c14

Docker Image

The Docker image for this release has been published to DockerHub:

Repository: gooddata/gooddata-goodchanges

Tags:

  • gooddata/gooddata-goodchanges:0.25.5
  • gooddata/gooddata-goodchanges:latest

Pull Commands

# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.5

# Pull latest
docker pull gooddata/gooddata-goodchanges:latest

Run Command

docker run --rm gooddata/gooddata-goodchanges:0.25.5 [command]

Standalone Binaries

Download the binary for your platform from the assets below.

Platform Architecture Asset
Linux x86_64 goodchanges-linux-amd64.tar.gz
Linux ARM64 goodchanges-linux-arm64.tar.gz
macOS Intel goodchanges-darwin-amd64.tar.gz
macOS Apple Silicon goodchanges-darwin-arm64.tar.gz
Windows x86_64 goodchanges-windows-amd64.zip
Windows ARM64 goodchanges-windows-arm64.zip
All platforms
  • goodchanges-darwin-amd64.tar.gz
  • goodchanges-darwin-amd64.tar.gz.sha256
  • goodchanges-darwin-arm64.tar.gz
  • goodchanges-darwin-arm64.tar.gz.sha256
  • goodchanges-linux-amd64.tar.gz
  • goodchanges-linux-amd64.tar.gz.sha256
  • goodchanges-linux-arm64.tar.gz
  • goodchanges-linux-arm64.tar.gz.sha256
  • goodchanges-windows-amd64.zip
  • goodchanges-windows-amd64.zip.sha256
  • goodchanges-windows-arm64.zip
  • goodchanges-windows-arm64.zip.sha256

Install (Linux/macOS)

# Example: download and install linux/amd64
curl -sL https://github.com/gooddata/gooddata-goodchanges/releases/download/v0.25.5/goodchanges-linux-amd64.tar.gz | tar xz
chmod +x goodchanges-linux-amd64
sudo mv goodchanges-linux-amd64 /usr/local/bin/goodchanges

Changelog

[0.25.5] - 2026-08-10

Fixed

  • Import-time side-effect taint now propagates transitively through import and re-export edges. A file whose change ran at import time (a changed top-level statement like console.log(...), or a bare import "x") tainted its direct importers, but the "runs at import time" quality was lost after one hop: a barrel that re-exports the side-effectful module (export { … } from "./api/ai/ai.js" in an entrypoint index.ts) only picked up the re-exported symbols, so consumers importing the barrel for other symbols were missed — e.g. a console.log added to e2e-utils/src/api/ai/ai.ts flagged only the 5 consumers of the ai exports instead of all 8 e2e-utils consumers (importing the barrel loads ai.ts and executes the statement regardless of which symbol is used). Such changes now carry a __side-effect__ sentinel that flows through every import/re-export edge, marking each file it reaches as wholly tainted and itself side-effectful, so barrels become side-effectful and all their consumers are flagged — the same result as adding the statement to the entrypoint directly. This is deliberately assume-the-worst; a TODO in the propagation notes the follow-up to refine it using each package's package.json "sideEffects" field (a side-effect-free module is tree-shaken and should not propagate).

[0.25.4] - 2026-08-07

Fixed

  • Intra-file taint propagation now matches symbol names as whole identifiers instead of raw substrings. The dependency/usage checks used strings.Contains(body, name), so a symbol whose name is a substring of another was falsely linked — e.g. removing the unused chooseAction tainted the surviving, used chooseActionByIndex (its body "contains" the string chooseAction), which then propagated to a spec and flagged gdc-dashboards-e2e for a dead-code deletion. All six propagation sites (astdiff.go intra-file graph, plus the seed/importer/usage propagation in analyzer.go) now require the name to appear flanked by non-identifier characters ([A-Za-z0-9_$]) via a shared containsIdentifier helper; non-identifier tokens like the * wildcard keep the substring behaviour. Strictly more precise (it only drops matches that were substrings inside a larger identifier — never a real usage), so no false negatives.

[0.25.3] - 2026-08-07

Fixed

  • Replaced the fine-grained path's whole-file * fallback with import-time-aware detection, so an empty symbol diff no longer floods every importer. Previously, when a changed file produced no symbol-level diff, FindAffectedFiles tainted the entire file with * — so a comment-only edit, a formatting change, a type-only change, or reordering imports in a widely-imported helper flagged all its consumers. That blanket is removed; a change now wildcards a file only when something that actually runs at import time changed:
    • a top-level side-effect statement (console.log(...), describe(...), test(...), etc.) — already detected via hasSideEffectStmtChanges;
    • a bare side-effect import (import "./x") added, removed, or re-pointed.
  • Named-import re-pointing is now handled precisely instead of via the blanket. When a binding keeps its name but resolves to a different module/export (import { x } from "./a" → "./b", or { a as x } → { b as x }), its usages don't change textually and the symbol diff missed them; those usages are now tainted directly. Reordering imports and re-pointing a type-only import (import type, when includeTypes is off) correctly taint nothing. Import statement type-only-ness is now tracked on tsparse.Import.

Together these keep genuine import-time changes flagged while eliminating the large false-positive class where a comment or dead-code edit to a shared file re-ran every dependent target.

  • Removing an unused export no longer floods every importer with taint. A deleted symbol was logged but never recorded as a change, so the per-file AST diff returned no affected symbols — and in the fine-grained path (FindAffectedFiles) an empty diff falls through to tainting the whole file with *. Deleting one unused export from a widely-imported helper (e.g. ERROR_MESSAGE from gdc-ldm-modeler-e2e's playwright/helpers/selectors.ts) therefore tainted every file that imported it, flagging all ~48 dependent specs. Deleted symbols are now recorded as changed and propagate by name, so a removed export taints exactly the files that imported that symbol: an unused one taints nobody, while a removed used export still flags its importers (no false negative). The deleted names are appended after intra-file propagation (which walks only surviving symbols) and before the whole-file side-effect fallback, so a deletion-only change is carried precisely instead of being widened.

[0.25.1] - 2026-08-07

Fixed

  • Destructuring default initializers are now included in a binding's taint span. A binding's default (const { a = compute() } = obj, const [a = fallbackVal] = arr) is a real second dependency — it supplies the value when the destructured slot is undefined — but it lives on the pattern (LHS), disjoint from the mapped source (RHS), so the element-wise span recorded in 0.25.0 covered only the source and excluded the default. A symbol used only inside such a default therefore escaped findTaintedSymbolsByUsage — a false negative. The binding's span is now widened to cover the default expression as well as its mapped source. (Narrow in scope: only bites when a tainted symbol appears solely in a binding default and nowhere else in the file, but false negatives are always worth closing.)

[0.25.0] - 2026-08-03

Changed

  • Destructured bindings are now attributed element-wise when the initializer is an array or object literal, refining the whole-initializer attribution added in 0.24.13. Previously every binding in const [a, b] = [tainted(), safe()] (or const { a, b } = { a: tainted(), b: safe() }) shared the entire initializer's span, so a change touching only one element tainted all the bindings — a false positive. Each binding is now mapped to its corresponding element (by index for array literals, by key for object literals, following nested patterns), and only falls back to the shared initializer span when mapping can't be done statically: a non-literal initializer (a call/identifier/member access — e.g. createStore(), where all bindings genuinely share the dependency), rest bindings (...rest), spreads, computed keys, or out-of-range indices. Net effect: a depends on tainted() and b on safe(), so a change to safe() no longer flags consumers of a.

[0.24.13] - 2026-08-01

Fixed

  • Destructuring variable declarations (const { a, b } = expr / const [a, b] = expr) are now decomposed into their individual bindings for both symbol and export extraction. Previously getDeclName only handled plain identifiers and returned "" for binding patterns, so destructured names were registered as neither symbols nor exports — they simply didn't exist in the file model. That silently broke taint propagation through a very common pattern: e.g. export const { store, startSagas, … } = createStore() in gdc-analytical-designer-runtime's reduxStore.ts. A runtime change to the reducers feeding createStore tainted createStore, but the taint could not reach store/startSagas/…, so the package reported 0 affected exports and nothing propagated to the AD module/harness (or the dashboards harness that embeds it) — a false negative. Each destructured binding is now emitted as a symbol/export; renames ({ a: b } → b), rest elements (...x), nested patterns and array holes are handled. To stay precise, a binding's symbol span is the initializer expression (not the whole statement), so the sibling binding names don't bleed into the compared body and cross-link in the AST diff — each binding is attributed exactly to the initializer it destructures.

[0.24.12] - 2026-07-30

Fixed

  • App taint propagation (added in 0.24.1) no longer over-flags downstream targets. An affected app was seeded into the upst...
Read more

Release v0.25.4

Choose a tag to compare

@github-actions github-actions released this 07 Aug 17:25
4442e29

Docker Image

The Docker image for this release has been published to DockerHub:

Repository: gooddata/gooddata-goodchanges

Tags:

  • gooddata/gooddata-goodchanges:0.25.4
  • gooddata/gooddata-goodchanges:latest

Pull Commands

# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.4

# Pull latest
docker pull gooddata/gooddata-goodchanges:latest

Run Command

docker run --rm gooddata/gooddata-goodchanges:0.25.4 [command]

Standalone Binaries

Download the binary for your platform from the assets below.

Platform Architecture Asset
Linux x86_64 goodchanges-linux-amd64.tar.gz
Linux ARM64 goodchanges-linux-arm64.tar.gz
macOS Intel goodchanges-darwin-amd64.tar.gz
macOS Apple Silicon goodchanges-darwin-arm64.tar.gz
Windows x86_64 goodchanges-windows-amd64.zip
Windows ARM64 goodchanges-windows-arm64.zip
All platforms
  • goodchanges-darwin-amd64.tar.gz
  • goodchanges-darwin-amd64.tar.gz.sha256
  • goodchanges-darwin-arm64.tar.gz
  • goodchanges-darwin-arm64.tar.gz.sha256
  • goodchanges-linux-amd64.tar.gz
  • goodchanges-linux-amd64.tar.gz.sha256
  • goodchanges-linux-arm64.tar.gz
  • goodchanges-linux-arm64.tar.gz.sha256
  • goodchanges-windows-amd64.zip
  • goodchanges-windows-amd64.zip.sha256
  • goodchanges-windows-arm64.zip
  • goodchanges-windows-arm64.zip.sha256

Install (Linux/macOS)

# Example: download and install linux/amd64
curl -sL https://github.com/gooddata/gooddata-goodchanges/releases/download/v0.25.4/goodchanges-linux-amd64.tar.gz | tar xz
chmod +x goodchanges-linux-amd64
sudo mv goodchanges-linux-amd64 /usr/local/bin/goodchanges

Changelog

[0.25.4] - 2026-08-07

Fixed

  • Intra-file taint propagation now matches symbol names as whole identifiers instead of raw substrings. The dependency/usage checks used strings.Contains(body, name), so a symbol whose name is a substring of another was falsely linked — e.g. removing the unused chooseAction tainted the surviving, used chooseActionByIndex (its body "contains" the string chooseAction), which then propagated to a spec and flagged gdc-dashboards-e2e for a dead-code deletion. All six propagation sites (astdiff.go intra-file graph, plus the seed/importer/usage propagation in analyzer.go) now require the name to appear flanked by non-identifier characters ([A-Za-z0-9_$]) via a shared containsIdentifier helper; non-identifier tokens like the * wildcard keep the substring behaviour. Strictly more precise (it only drops matches that were substrings inside a larger identifier — never a real usage), so no false negatives.

[0.25.3] - 2026-08-07

Fixed

  • Replaced the fine-grained path's whole-file * fallback with import-time-aware detection, so an empty symbol diff no longer floods every importer. Previously, when a changed file produced no symbol-level diff, FindAffectedFiles tainted the entire file with * — so a comment-only edit, a formatting change, a type-only change, or reordering imports in a widely-imported helper flagged all its consumers. That blanket is removed; a change now wildcards a file only when something that actually runs at import time changed:
    • a top-level side-effect statement (console.log(...), describe(...), test(...), etc.) — already detected via hasSideEffectStmtChanges;
    • a bare side-effect import (import "./x") added, removed, or re-pointed.
  • Named-import re-pointing is now handled precisely instead of via the blanket. When a binding keeps its name but resolves to a different module/export (import { x } from "./a" → "./b", or { a as x } → { b as x }), its usages don't change textually and the symbol diff missed them; those usages are now tainted directly. Reordering imports and re-pointing a type-only import (import type, when includeTypes is off) correctly taint nothing. Import statement type-only-ness is now tracked on tsparse.Import.

Together these keep genuine import-time changes flagged while eliminating the large false-positive class where a comment or dead-code edit to a shared file re-ran every dependent target.

  • Removing an unused export no longer floods every importer with taint. A deleted symbol was logged but never recorded as a change, so the per-file AST diff returned no affected symbols — and in the fine-grained path (FindAffectedFiles) an empty diff falls through to tainting the whole file with *. Deleting one unused export from a widely-imported helper (e.g. ERROR_MESSAGE from gdc-ldm-modeler-e2e's playwright/helpers/selectors.ts) therefore tainted every file that imported it, flagging all ~48 dependent specs. Deleted symbols are now recorded as changed and propagate by name, so a removed export taints exactly the files that imported that symbol: an unused one taints nobody, while a removed used export still flags its importers (no false negative). The deleted names are appended after intra-file propagation (which walks only surviving symbols) and before the whole-file side-effect fallback, so a deletion-only change is carried precisely instead of being widened.

[0.25.1] - 2026-08-07

Fixed

  • Destructuring default initializers are now included in a binding's taint span. A binding's default (const { a = compute() } = obj, const [a = fallbackVal] = arr) is a real second dependency — it supplies the value when the destructured slot is undefined — but it lives on the pattern (LHS), disjoint from the mapped source (RHS), so the element-wise span recorded in 0.25.0 covered only the source and excluded the default. A symbol used only inside such a default therefore escaped findTaintedSymbolsByUsage — a false negative. The binding's span is now widened to cover the default expression as well as its mapped source. (Narrow in scope: only bites when a tainted symbol appears solely in a binding default and nowhere else in the file, but false negatives are always worth closing.)

[0.25.0] - 2026-08-03

Changed

  • Destructured bindings are now attributed element-wise when the initializer is an array or object literal, refining the whole-initializer attribution added in 0.24.13. Previously every binding in const [a, b] = [tainted(), safe()] (or const { a, b } = { a: tainted(), b: safe() }) shared the entire initializer's span, so a change touching only one element tainted all the bindings — a false positive. Each binding is now mapped to its corresponding element (by index for array literals, by key for object literals, following nested patterns), and only falls back to the shared initializer span when mapping can't be done statically: a non-literal initializer (a call/identifier/member access — e.g. createStore(), where all bindings genuinely share the dependency), rest bindings (...rest), spreads, computed keys, or out-of-range indices. Net effect: a depends on tainted() and b on safe(), so a change to safe() no longer flags consumers of a.

[0.24.13] - 2026-08-01

Fixed

  • Destructuring variable declarations (const { a, b } = expr / const [a, b] = expr) are now decomposed into their individual bindings for both symbol and export extraction. Previously getDeclName only handled plain identifiers and returned "" for binding patterns, so destructured names were registered as neither symbols nor exports — they simply didn't exist in the file model. That silently broke taint propagation through a very common pattern: e.g. export const { store, startSagas, … } = createStore() in gdc-analytical-designer-runtime's reduxStore.ts. A runtime change to the reducers feeding createStore tainted createStore, but the taint could not reach store/startSagas/…, so the package reported 0 affected exports and nothing propagated to the AD module/harness (or the dashboards harness that embeds it) — a false negative. Each destructured binding is now emitted as a symbol/export; renames ({ a: b } → b), rest elements (...x), nested patterns and array holes are handled. To stay precise, a binding's symbol span is the initializer expression (not the whole statement), so the sibling binding names don't bleed into the compared body and cross-link in the AST diff — each binding is attributed exactly to the initializer it destructures.

[0.24.12] - 2026-07-30

Fixed

  • App taint propagation (added in 0.24.1) no longer over-flags downstream targets. An affected app was seeded into the upstream-taint map — tainting everything that imports it — whenever it was merely reachable in the affected set, i.e. a transitive dependent of any touched package. So a change to a library an app depends on flagged that app's harness/host consumers even when the change affected no export the app actually imports (e.g. removing unused exports from gdc-analytical-designer-runtime flagged gdc-analytical-designer-harness, gdc-dashboards-harness, and gdc-host-application). The wholesale-taint seed is now gated on the app being genuinely affected — the same conditions its own target detection uses: directly changed (has changed files), a changed lockfile dependency, or an actual tainted import from upstream (HasTaintedImportsForGlob). Apps that are only transitively reachable but import nothing that changed no longer propagate taint; real changes to exports an app imports still propagate as before. The per-package upstream-taint filter is also factored into a shared buildPkgUpstreamTaint helper used by both the library and app paths.

[0.24.11] - 2026-07-30

Changed

[0.24.10] - 2026-07-30

Fixed

  • Symbol-level AST diff no longer marks an unchanged declaration as changed when ...
Read more

Release v0.25.3

Choose a tag to compare

@github-actions github-actions released this 07 Aug 16:33
b11b5d2

Docker Image

The Docker image for this release has been published to DockerHub:

Repository: gooddata/gooddata-goodchanges

Tags:

  • gooddata/gooddata-goodchanges:0.25.3
  • gooddata/gooddata-goodchanges:latest

Pull Commands

# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.3

# Pull latest
docker pull gooddata/gooddata-goodchanges:latest

Run Command

docker run --rm gooddata/gooddata-goodchanges:0.25.3 [command]

Standalone Binaries

Download the binary for your platform from the assets below.

Platform Architecture Asset
Linux x86_64 goodchanges-linux-amd64.tar.gz
Linux ARM64 goodchanges-linux-arm64.tar.gz
macOS Intel goodchanges-darwin-amd64.tar.gz
macOS Apple Silicon goodchanges-darwin-arm64.tar.gz
Windows x86_64 goodchanges-windows-amd64.zip
Windows ARM64 goodchanges-windows-arm64.zip
All platforms
  • goodchanges-darwin-amd64.tar.gz
  • goodchanges-darwin-amd64.tar.gz.sha256
  • goodchanges-darwin-arm64.tar.gz
  • goodchanges-darwin-arm64.tar.gz.sha256
  • goodchanges-linux-amd64.tar.gz
  • goodchanges-linux-amd64.tar.gz.sha256
  • goodchanges-linux-arm64.tar.gz
  • goodchanges-linux-arm64.tar.gz.sha256
  • goodchanges-windows-amd64.zip
  • goodchanges-windows-amd64.zip.sha256
  • goodchanges-windows-arm64.zip
  • goodchanges-windows-arm64.zip.sha256

Install (Linux/macOS)

# Example: download and install linux/amd64
curl -sL https://github.com/gooddata/gooddata-goodchanges/releases/download/v0.25.3/goodchanges-linux-amd64.tar.gz | tar xz
chmod +x goodchanges-linux-amd64
sudo mv goodchanges-linux-amd64 /usr/local/bin/goodchanges

Changelog

[0.25.3] - 2026-08-07

Fixed

  • Replaced the fine-grained path's whole-file * fallback with import-time-aware detection, so an empty symbol diff no longer floods every importer. Previously, when a changed file produced no symbol-level diff, FindAffectedFiles tainted the entire file with * — so a comment-only edit, a formatting change, a type-only change, or reordering imports in a widely-imported helper flagged all its consumers. That blanket is removed; a change now wildcards a file only when something that actually runs at import time changed:
    • a top-level side-effect statement (console.log(...), describe(...), test(...), etc.) — already detected via hasSideEffectStmtChanges;
    • a bare side-effect import (import "./x") added, removed, or re-pointed.
  • Named-import re-pointing is now handled precisely instead of via the blanket. When a binding keeps its name but resolves to a different module/export (import { x } from "./a" → "./b", or { a as x } → { b as x }), its usages don't change textually and the symbol diff missed them; those usages are now tainted directly. Reordering imports and re-pointing a type-only import (import type, when includeTypes is off) correctly taint nothing. Import statement type-only-ness is now tracked on tsparse.Import.

Together these keep genuine import-time changes flagged while eliminating the large false-positive class where a comment or dead-code edit to a shared file re-ran every dependent target.

  • Removing an unused export no longer floods every importer with taint. A deleted symbol was logged but never recorded as a change, so the per-file AST diff returned no affected symbols — and in the fine-grained path (FindAffectedFiles) an empty diff falls through to tainting the whole file with *. Deleting one unused export from a widely-imported helper (e.g. ERROR_MESSAGE from gdc-ldm-modeler-e2e's playwright/helpers/selectors.ts) therefore tainted every file that imported it, flagging all ~48 dependent specs. Deleted symbols are now recorded as changed and propagate by name, so a removed export taints exactly the files that imported that symbol: an unused one taints nobody, while a removed used export still flags its importers (no false negative). The deleted names are appended after intra-file propagation (which walks only surviving symbols) and before the whole-file side-effect fallback, so a deletion-only change is carried precisely instead of being widened.

[0.25.1] - 2026-08-07

Fixed

  • Destructuring default initializers are now included in a binding's taint span. A binding's default (const { a = compute() } = obj, const [a = fallbackVal] = arr) is a real second dependency — it supplies the value when the destructured slot is undefined — but it lives on the pattern (LHS), disjoint from the mapped source (RHS), so the element-wise span recorded in 0.25.0 covered only the source and excluded the default. A symbol used only inside such a default therefore escaped findTaintedSymbolsByUsage — a false negative. The binding's span is now widened to cover the default expression as well as its mapped source. (Narrow in scope: only bites when a tainted symbol appears solely in a binding default and nowhere else in the file, but false negatives are always worth closing.)

[0.25.0] - 2026-08-03

Changed

  • Destructured bindings are now attributed element-wise when the initializer is an array or object literal, refining the whole-initializer attribution added in 0.24.13. Previously every binding in const [a, b] = [tainted(), safe()] (or const { a, b } = { a: tainted(), b: safe() }) shared the entire initializer's span, so a change touching only one element tainted all the bindings — a false positive. Each binding is now mapped to its corresponding element (by index for array literals, by key for object literals, following nested patterns), and only falls back to the shared initializer span when mapping can't be done statically: a non-literal initializer (a call/identifier/member access — e.g. createStore(), where all bindings genuinely share the dependency), rest bindings (...rest), spreads, computed keys, or out-of-range indices. Net effect: a depends on tainted() and b on safe(), so a change to safe() no longer flags consumers of a.

[0.24.13] - 2026-08-01

Fixed

  • Destructuring variable declarations (const { a, b } = expr / const [a, b] = expr) are now decomposed into their individual bindings for both symbol and export extraction. Previously getDeclName only handled plain identifiers and returned "" for binding patterns, so destructured names were registered as neither symbols nor exports — they simply didn't exist in the file model. That silently broke taint propagation through a very common pattern: e.g. export const { store, startSagas, … } = createStore() in gdc-analytical-designer-runtime's reduxStore.ts. A runtime change to the reducers feeding createStore tainted createStore, but the taint could not reach store/startSagas/…, so the package reported 0 affected exports and nothing propagated to the AD module/harness (or the dashboards harness that embeds it) — a false negative. Each destructured binding is now emitted as a symbol/export; renames ({ a: b } → b), rest elements (...x), nested patterns and array holes are handled. To stay precise, a binding's symbol span is the initializer expression (not the whole statement), so the sibling binding names don't bleed into the compared body and cross-link in the AST diff — each binding is attributed exactly to the initializer it destructures.

[0.24.12] - 2026-07-30

Fixed

  • App taint propagation (added in 0.24.1) no longer over-flags downstream targets. An affected app was seeded into the upstream-taint map — tainting everything that imports it — whenever it was merely reachable in the affected set, i.e. a transitive dependent of any touched package. So a change to a library an app depends on flagged that app's harness/host consumers even when the change affected no export the app actually imports (e.g. removing unused exports from gdc-analytical-designer-runtime flagged gdc-analytical-designer-harness, gdc-dashboards-harness, and gdc-host-application). The wholesale-taint seed is now gated on the app being genuinely affected — the same conditions its own target detection uses: directly changed (has changed files), a changed lockfile dependency, or an actual tainted import from upstream (HasTaintedImportsForGlob). Apps that are only transitively reachable but import nothing that changed no longer propagate taint; real changes to exports an app imports still propagate as before. The per-package upstream-taint filter is also factored into a shared buildPkgUpstreamTaint helper used by both the library and app paths.

[0.24.11] - 2026-07-30

Changed

[0.24.10] - 2026-07-30

Fixed

  • Symbol-level AST diff no longer marks an unchanged declaration as changed when a neighboring declaration is edited or removed. Each symbol's body text was extracted starting at stmt.Pos(), which in the TS AST includes the symbol's leading trivia (the preceding comments and blank lines). Deleting a sibling — or otherwise changing what comes before a symbol — re-attaches the intervening comment (e.g. a // SECTION header) to the next symbol, so its extracted body differed between the old and new versions and it was reported as a runtime change. That false "change" then spread through the intra-file reference graph and out via the library's exports, over-tainting downstream consumers. Symbol start lines are now taken from the first real token (scanner.SkipTrivia), excluding leading comments/blank lines from the compared body. Example: removing three unused createSelector exports from bootstrap_selector.ts previously tainted nine untouched sibling selectors (and everything reachable from them); it now taints none.

[0...

Read more

Release v0.25.2

Choose a tag to compare

@github-actions github-actions released this 07 Aug 15:56
deda71e

Docker Image

The Docker image for this release has been published to DockerHub:

Repository: gooddata/gooddata-goodchanges

Tags:

  • gooddata/gooddata-goodchanges:0.25.2
  • gooddata/gooddata-goodchanges:latest

Pull Commands

# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.2

# Pull latest
docker pull gooddata/gooddata-goodchanges:latest

Run Command

docker run --rm gooddata/gooddata-goodchanges:0.25.2 [command]

Standalone Binaries

Download the binary for your platform from the assets below.

Platform Architecture Asset
Linux x86_64 goodchanges-linux-amd64.tar.gz
Linux ARM64 goodchanges-linux-arm64.tar.gz
macOS Intel goodchanges-darwin-amd64.tar.gz
macOS Apple Silicon goodchanges-darwin-arm64.tar.gz
Windows x86_64 goodchanges-windows-amd64.zip
Windows ARM64 goodchanges-windows-arm64.zip
All platforms
  • goodchanges-darwin-amd64.tar.gz
  • goodchanges-darwin-amd64.tar.gz.sha256
  • goodchanges-darwin-arm64.tar.gz
  • goodchanges-darwin-arm64.tar.gz.sha256
  • goodchanges-linux-amd64.tar.gz
  • goodchanges-linux-amd64.tar.gz.sha256
  • goodchanges-linux-arm64.tar.gz
  • goodchanges-linux-arm64.tar.gz.sha256
  • goodchanges-windows-amd64.zip
  • goodchanges-windows-amd64.zip.sha256
  • goodchanges-windows-arm64.zip
  • goodchanges-windows-arm64.zip.sha256

Install (Linux/macOS)

# Example: download and install linux/amd64
curl -sL https://github.com/gooddata/gooddata-goodchanges/releases/download/v0.25.2/goodchanges-linux-amd64.tar.gz | tar xz
chmod +x goodchanges-linux-amd64
sudo mv goodchanges-linux-amd64 /usr/local/bin/goodchanges

Changelog

[0.25.2] - 2026-08-07

Fixed

  • Removing an unused export no longer floods every importer with taint. A deleted symbol was logged but never recorded as a change, so the per-file AST diff returned no affected symbols — and in the fine-grained path (FindAffectedFiles) an empty diff falls through to tainting the whole file with *. Deleting one unused export from a widely-imported helper (e.g. ERROR_MESSAGE from gdc-ldm-modeler-e2e's playwright/helpers/selectors.ts) therefore tainted every file that imported it, flagging all ~48 dependent specs. Deleted symbols are now recorded as changed and propagate by name, so a removed export taints exactly the files that imported that symbol: an unused one taints nobody, while a removed used export still flags its importers (no false negative). The deleted names are appended after intra-file propagation (which walks only surviving symbols) and before the whole-file side-effect fallback, so a deletion-only change is carried precisely instead of being widened.

[0.25.1] - 2026-08-07

Fixed

  • Destructuring default initializers are now included in a binding's taint span. A binding's default (const { a = compute() } = obj, const [a = fallbackVal] = arr) is a real second dependency — it supplies the value when the destructured slot is undefined — but it lives on the pattern (LHS), disjoint from the mapped source (RHS), so the element-wise span recorded in 0.25.0 covered only the source and excluded the default. A symbol used only inside such a default therefore escaped findTaintedSymbolsByUsage — a false negative. The binding's span is now widened to cover the default expression as well as its mapped source. (Narrow in scope: only bites when a tainted symbol appears solely in a binding default and nowhere else in the file, but false negatives are always worth closing.)

[0.25.0] - 2026-08-03

Changed

  • Destructured bindings are now attributed element-wise when the initializer is an array or object literal, refining the whole-initializer attribution added in 0.24.13. Previously every binding in const [a, b] = [tainted(), safe()] (or const { a, b } = { a: tainted(), b: safe() }) shared the entire initializer's span, so a change touching only one element tainted all the bindings — a false positive. Each binding is now mapped to its corresponding element (by index for array literals, by key for object literals, following nested patterns), and only falls back to the shared initializer span when mapping can't be done statically: a non-literal initializer (a call/identifier/member access — e.g. createStore(), where all bindings genuinely share the dependency), rest bindings (...rest), spreads, computed keys, or out-of-range indices. Net effect: a depends on tainted() and b on safe(), so a change to safe() no longer flags consumers of a.

[0.24.13] - 2026-08-01

Fixed

  • Destructuring variable declarations (const { a, b } = expr / const [a, b] = expr) are now decomposed into their individual bindings for both symbol and export extraction. Previously getDeclName only handled plain identifiers and returned "" for binding patterns, so destructured names were registered as neither symbols nor exports — they simply didn't exist in the file model. That silently broke taint propagation through a very common pattern: e.g. export const { store, startSagas, … } = createStore() in gdc-analytical-designer-runtime's reduxStore.ts. A runtime change to the reducers feeding createStore tainted createStore, but the taint could not reach store/startSagas/…, so the package reported 0 affected exports and nothing propagated to the AD module/harness (or the dashboards harness that embeds it) — a false negative. Each destructured binding is now emitted as a symbol/export; renames ({ a: b } → b), rest elements (...x), nested patterns and array holes are handled. To stay precise, a binding's symbol span is the initializer expression (not the whole statement), so the sibling binding names don't bleed into the compared body and cross-link in the AST diff — each binding is attributed exactly to the initializer it destructures.

[0.24.12] - 2026-07-30

Fixed

  • App taint propagation (added in 0.24.1) no longer over-flags downstream targets. An affected app was seeded into the upstream-taint map — tainting everything that imports it — whenever it was merely reachable in the affected set, i.e. a transitive dependent of any touched package. So a change to a library an app depends on flagged that app's harness/host consumers even when the change affected no export the app actually imports (e.g. removing unused exports from gdc-analytical-designer-runtime flagged gdc-analytical-designer-harness, gdc-dashboards-harness, and gdc-host-application). The wholesale-taint seed is now gated on the app being genuinely affected — the same conditions its own target detection uses: directly changed (has changed files), a changed lockfile dependency, or an actual tainted import from upstream (HasTaintedImportsForGlob). Apps that are only transitively reachable but import nothing that changed no longer propagate taint; real changes to exports an app imports still propagate as before. The per-package upstream-taint filter is also factored into a shared buildPkgUpstreamTaint helper used by both the library and app paths.

[0.24.11] - 2026-07-30

Changed

[0.24.10] - 2026-07-30

Fixed

  • Symbol-level AST diff no longer marks an unchanged declaration as changed when a neighboring declaration is edited or removed. Each symbol's body text was extracted starting at stmt.Pos(), which in the TS AST includes the symbol's leading trivia (the preceding comments and blank lines). Deleting a sibling — or otherwise changing what comes before a symbol — re-attaches the intervening comment (e.g. a // SECTION header) to the next symbol, so its extracted body differed between the old and new versions and it was reported as a runtime change. That false "change" then spread through the intra-file reference graph and out via the library's exports, over-tainting downstream consumers. Symbol start lines are now taken from the first real token (scanner.SkipTrivia), excluding leading comments/blank lines from the compared body. Example: removing three unused createSelector exports from bootstrap_selector.ts previously tainted nine untouched sibling selectors (and everything reachable from them); it now taints none.

[0.24.9] - 2026-07-30

Changed

  • Renamed the LOG_LEVEL=BASIC/DEBUG summary line Affected e2e packages (N): to Affected targets (N):. The list has always contained resolved .goodchangesrc.json targets (unit-test targets, app/harness targets, etc.), not just e2e packages, so the label now reflects its actual contents. Log wording only — the JSON result on stdout is unchanged.

[0.24.8] - 2026-07-23

Changed

[0.24.7] - 2026-07-23

Changed

[0.24.6] - 2026-07-23

Changed

[0.24.5] - 2026-07-23

Changed

[0.24.4] - 2026-07-23

Changed

[0.24.3] - 2026-07-23

Changed

Read more

Release v0.25.1

Choose a tag to compare

@github-actions github-actions released this 07 Aug 15:54
6c0ed9a

Docker Image

The Docker image for this release has been published to DockerHub:

Repository: gooddata/gooddata-goodchanges

Tags:

  • gooddata/gooddata-goodchanges:0.25.1
  • gooddata/gooddata-goodchanges:latest

Pull Commands

# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.1

# Pull latest
docker pull gooddata/gooddata-goodchanges:latest

Run Command

docker run --rm gooddata/gooddata-goodchanges:0.25.1 [command]

Standalone Binaries

Download the binary for your platform from the assets below.

Platform Architecture Asset
Linux x86_64 goodchanges-linux-amd64.tar.gz
Linux ARM64 goodchanges-linux-arm64.tar.gz
macOS Intel goodchanges-darwin-amd64.tar.gz
macOS Apple Silicon goodchanges-darwin-arm64.tar.gz
Windows x86_64 goodchanges-windows-amd64.zip
Windows ARM64 goodchanges-windows-arm64.zip
All platforms
  • goodchanges-darwin-amd64.tar.gz
  • goodchanges-darwin-amd64.tar.gz.sha256
  • goodchanges-darwin-arm64.tar.gz
  • goodchanges-darwin-arm64.tar.gz.sha256
  • goodchanges-linux-amd64.tar.gz
  • goodchanges-linux-amd64.tar.gz.sha256
  • goodchanges-linux-arm64.tar.gz
  • goodchanges-linux-arm64.tar.gz.sha256
  • goodchanges-windows-amd64.zip
  • goodchanges-windows-amd64.zip.sha256
  • goodchanges-windows-arm64.zip
  • goodchanges-windows-arm64.zip.sha256

Install (Linux/macOS)

# Example: download and install linux/amd64
curl -sL https://github.com/gooddata/gooddata-goodchanges/releases/download/v0.25.1/goodchanges-linux-amd64.tar.gz | tar xz
chmod +x goodchanges-linux-amd64
sudo mv goodchanges-linux-amd64 /usr/local/bin/goodchanges

Changelog

[0.25.1] - 2026-08-07

Fixed

  • Destructuring default initializers are now included in a binding's taint span. A binding's default (const { a = compute() } = obj, const [a = fallbackVal] = arr) is a real second dependency — it supplies the value when the destructured slot is undefined — but it lives on the pattern (LHS), disjoint from the mapped source (RHS), so the element-wise span recorded in 0.25.0 covered only the source and excluded the default. A symbol used only inside such a default therefore escaped findTaintedSymbolsByUsage — a false negative. The binding's span is now widened to cover the default expression as well as its mapped source. (Narrow in scope: only bites when a tainted symbol appears solely in a binding default and nowhere else in the file, but false negatives are always worth closing.)

[0.25.0] - 2026-08-03

Changed

  • Destructured bindings are now attributed element-wise when the initializer is an array or object literal, refining the whole-initializer attribution added in 0.24.13. Previously every binding in const [a, b] = [tainted(), safe()] (or const { a, b } = { a: tainted(), b: safe() }) shared the entire initializer's span, so a change touching only one element tainted all the bindings — a false positive. Each binding is now mapped to its corresponding element (by index for array literals, by key for object literals, following nested patterns), and only falls back to the shared initializer span when mapping can't be done statically: a non-literal initializer (a call/identifier/member access — e.g. createStore(), where all bindings genuinely share the dependency), rest bindings (...rest), spreads, computed keys, or out-of-range indices. Net effect: a depends on tainted() and b on safe(), so a change to safe() no longer flags consumers of a.

[0.24.13] - 2026-08-01

Fixed

  • Destructuring variable declarations (const { a, b } = expr / const [a, b] = expr) are now decomposed into their individual bindings for both symbol and export extraction. Previously getDeclName only handled plain identifiers and returned "" for binding patterns, so destructured names were registered as neither symbols nor exports — they simply didn't exist in the file model. That silently broke taint propagation through a very common pattern: e.g. export const { store, startSagas, … } = createStore() in gdc-analytical-designer-runtime's reduxStore.ts. A runtime change to the reducers feeding createStore tainted createStore, but the taint could not reach store/startSagas/…, so the package reported 0 affected exports and nothing propagated to the AD module/harness (or the dashboards harness that embeds it) — a false negative. Each destructured binding is now emitted as a symbol/export; renames ({ a: b } → b), rest elements (...x), nested patterns and array holes are handled. To stay precise, a binding's symbol span is the initializer expression (not the whole statement), so the sibling binding names don't bleed into the compared body and cross-link in the AST diff — each binding is attributed exactly to the initializer it destructures.

[0.24.12] - 2026-07-30

Fixed

  • App taint propagation (added in 0.24.1) no longer over-flags downstream targets. An affected app was seeded into the upstream-taint map — tainting everything that imports it — whenever it was merely reachable in the affected set, i.e. a transitive dependent of any touched package. So a change to a library an app depends on flagged that app's harness/host consumers even when the change affected no export the app actually imports (e.g. removing unused exports from gdc-analytical-designer-runtime flagged gdc-analytical-designer-harness, gdc-dashboards-harness, and gdc-host-application). The wholesale-taint seed is now gated on the app being genuinely affected — the same conditions its own target detection uses: directly changed (has changed files), a changed lockfile dependency, or an actual tainted import from upstream (HasTaintedImportsForGlob). Apps that are only transitively reachable but import nothing that changed no longer propagate taint; real changes to exports an app imports still propagate as before. The per-package upstream-taint filter is also factored into a shared buildPkgUpstreamTaint helper used by both the library and app paths.

[0.24.11] - 2026-07-30

Changed

[0.24.10] - 2026-07-30

Fixed

  • Symbol-level AST diff no longer marks an unchanged declaration as changed when a neighboring declaration is edited or removed. Each symbol's body text was extracted starting at stmt.Pos(), which in the TS AST includes the symbol's leading trivia (the preceding comments and blank lines). Deleting a sibling — or otherwise changing what comes before a symbol — re-attaches the intervening comment (e.g. a // SECTION header) to the next symbol, so its extracted body differed between the old and new versions and it was reported as a runtime change. That false "change" then spread through the intra-file reference graph and out via the library's exports, over-tainting downstream consumers. Symbol start lines are now taken from the first real token (scanner.SkipTrivia), excluding leading comments/blank lines from the compared body. Example: removing three unused createSelector exports from bootstrap_selector.ts previously tainted nine untouched sibling selectors (and everything reachable from them); it now taints none.

[0.24.9] - 2026-07-30

Changed

  • Renamed the LOG_LEVEL=BASIC/DEBUG summary line Affected e2e packages (N): to Affected targets (N):. The list has always contained resolved .goodchangesrc.json targets (unit-test targets, app/harness targets, etc.), not just e2e packages, so the label now reflects its actual contents. Log wording only — the JSON result on stdout is unchanged.

[0.24.8] - 2026-07-23

Changed

[0.24.7] - 2026-07-23

Changed

[0.24.6] - 2026-07-23

Changed

[0.24.5] - 2026-07-23

Changed

[0.24.4] - 2026-07-23

Changed

[0.24.3] - 2026-07-23

Changed

[0.24.2] - 2026-07-23

Changed

[0.24.1] - 2026-07-21

Fixed

  • Affected apps now propagate taint to packages that import them. When a package is not a library, its per-symbol export analysis is skipped — but it was also seeding no taint at all, so the taint chain died at the app boundary. A consumer importing from an affected app (e.g. a thin harness that dynamically imports its app package: () => import("gdc-analytical-designer-module")) saw a clean upstream-taint map and was never flagged, so app/harness targets sitting behind an intermediate app went undetected. Now an affected app is tainted wholesale: all of its entrypoint exports are seeded into the upstream-taint map (mirroring the existing global-changeDirs full-taint seeding for libraries), so downstream importers match via the normal import graph — including bare/dynamic si...
Read more

Release v0.25.0

Choose a tag to compare

@github-actions github-actions released this 03 Aug 20:15
43aedea

Docker Image

The Docker image for this release has been published to DockerHub:

Repository: gooddata/gooddata-goodchanges

Tags:

  • gooddata/gooddata-goodchanges:0.25.0
  • gooddata/gooddata-goodchanges:latest

Pull Commands

# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.0

# Pull latest
docker pull gooddata/gooddata-goodchanges:latest

Run Command

docker run --rm gooddata/gooddata-goodchanges:0.25.0 [command]

Standalone Binaries

Download the binary for your platform from the assets below.

Platform Architecture Asset
Linux x86_64 goodchanges-linux-amd64.tar.gz
Linux ARM64 goodchanges-linux-arm64.tar.gz
macOS Intel goodchanges-darwin-amd64.tar.gz
macOS Apple Silicon goodchanges-darwin-arm64.tar.gz
Windows x86_64 goodchanges-windows-amd64.zip
Windows ARM64 goodchanges-windows-arm64.zip
All platforms
  • goodchanges-darwin-amd64.tar.gz
  • goodchanges-darwin-amd64.tar.gz.sha256
  • goodchanges-darwin-arm64.tar.gz
  • goodchanges-darwin-arm64.tar.gz.sha256
  • goodchanges-linux-amd64.tar.gz
  • goodchanges-linux-amd64.tar.gz.sha256
  • goodchanges-linux-arm64.tar.gz
  • goodchanges-linux-arm64.tar.gz.sha256
  • goodchanges-windows-amd64.zip
  • goodchanges-windows-amd64.zip.sha256
  • goodchanges-windows-arm64.zip
  • goodchanges-windows-arm64.zip.sha256

Install (Linux/macOS)

# Example: download and install linux/amd64
curl -sL https://github.com/gooddata/gooddata-goodchanges/releases/download/v0.25.0/goodchanges-linux-amd64.tar.gz | tar xz
chmod +x goodchanges-linux-amd64
sudo mv goodchanges-linux-amd64 /usr/local/bin/goodchanges

Changelog

[0.25.0] - 2026-08-03

Changed

  • Destructured bindings are now attributed element-wise when the initializer is an array or object literal, refining the whole-initializer attribution added in 0.24.13. Previously every binding in const [a, b] = [tainted(), safe()] (or const { a, b } = { a: tainted(), b: safe() }) shared the entire initializer's span, so a change touching only one element tainted all the bindings — a false positive. Each binding is now mapped to its corresponding element (by index for array literals, by key for object literals, following nested patterns), and only falls back to the shared initializer span when mapping can't be done statically: a non-literal initializer (a call/identifier/member access — e.g. createStore(), where all bindings genuinely share the dependency), rest bindings (...rest), spreads, computed keys, or out-of-range indices. Net effect: a depends on tainted() and b on safe(), so a change to safe() no longer flags consumers of a.

[0.24.13] - 2026-08-01

Fixed

  • Destructuring variable declarations (const { a, b } = expr / const [a, b] = expr) are now decomposed into their individual bindings for both symbol and export extraction. Previously getDeclName only handled plain identifiers and returned "" for binding patterns, so destructured names were registered as neither symbols nor exports — they simply didn't exist in the file model. That silently broke taint propagation through a very common pattern: e.g. export const { store, startSagas, … } = createStore() in gdc-analytical-designer-runtime's reduxStore.ts. A runtime change to the reducers feeding createStore tainted createStore, but the taint could not reach store/startSagas/…, so the package reported 0 affected exports and nothing propagated to the AD module/harness (or the dashboards harness that embeds it) — a false negative. Each destructured binding is now emitted as a symbol/export; renames ({ a: b } → b), rest elements (...x), nested patterns and array holes are handled. To stay precise, a binding's symbol span is the initializer expression (not the whole statement), so the sibling binding names don't bleed into the compared body and cross-link in the AST diff — each binding is attributed exactly to the initializer it destructures.

[0.24.12] - 2026-07-30

Fixed

  • App taint propagation (added in 0.24.1) no longer over-flags downstream targets. An affected app was seeded into the upstream-taint map — tainting everything that imports it — whenever it was merely reachable in the affected set, i.e. a transitive dependent of any touched package. So a change to a library an app depends on flagged that app's harness/host consumers even when the change affected no export the app actually imports (e.g. removing unused exports from gdc-analytical-designer-runtime flagged gdc-analytical-designer-harness, gdc-dashboards-harness, and gdc-host-application). The wholesale-taint seed is now gated on the app being genuinely affected — the same conditions its own target detection uses: directly changed (has changed files), a changed lockfile dependency, or an actual tainted import from upstream (HasTaintedImportsForGlob). Apps that are only transitively reachable but import nothing that changed no longer propagate taint; real changes to exports an app imports still propagate as before. The per-package upstream-taint filter is also factored into a shared buildPkgUpstreamTaint helper used by both the library and app paths.

[0.24.11] - 2026-07-30

Changed

[0.24.10] - 2026-07-30

Fixed

  • Symbol-level AST diff no longer marks an unchanged declaration as changed when a neighboring declaration is edited or removed. Each symbol's body text was extracted starting at stmt.Pos(), which in the TS AST includes the symbol's leading trivia (the preceding comments and blank lines). Deleting a sibling — or otherwise changing what comes before a symbol — re-attaches the intervening comment (e.g. a // SECTION header) to the next symbol, so its extracted body differed between the old and new versions and it was reported as a runtime change. That false "change" then spread through the intra-file reference graph and out via the library's exports, over-tainting downstream consumers. Symbol start lines are now taken from the first real token (scanner.SkipTrivia), excluding leading comments/blank lines from the compared body. Example: removing three unused createSelector exports from bootstrap_selector.ts previously tainted nine untouched sibling selectors (and everything reachable from them); it now taints none.

[0.24.9] - 2026-07-30

Changed

  • Renamed the LOG_LEVEL=BASIC/DEBUG summary line Affected e2e packages (N): to Affected targets (N):. The list has always contained resolved .goodchangesrc.json targets (unit-test targets, app/harness targets, etc.), not just e2e packages, so the label now reflects its actual contents. Log wording only — the JSON result on stdout is unchanged.

[0.24.8] - 2026-07-23

Changed

[0.24.7] - 2026-07-23

Changed

[0.24.6] - 2026-07-23

Changed

[0.24.5] - 2026-07-23

Changed

[0.24.4] - 2026-07-23

Changed

[0.24.3] - 2026-07-23

Changed

[0.24.2] - 2026-07-23

Changed

[0.24.1] - 2026-07-21

Fixed

  • Affected apps now propagate taint to packages that import them. When a package is not a library, its per-symbol export analysis is skipped — but it was also seeding no taint at all, so the taint chain died at the app boundary. A consumer importing from an affected app (e.g. a thin harness that dynamically imports its app package: () => import("gdc-analytical-designer-module")) saw a clean upstream-taint map and was never flagged, so app/harness targets sitting behind an intermediate app went undetected. Now an affected app is tainted wholesale: all of its entrypoint exports are seeded into the upstream-taint map (mirroring the existing global-changeDirs full-taint seeding for libraries), so downstream importers match via the normal import graph — including bare/dynamic side-effect imports (empty import names), which match on any non-empty symbol set for the package. Example: a change in gdc-analytical-designer-runtime now correctly reaches gdc-analytical-designer-harness through the intermediate gdc-analytical-designer-module app.

[0.24.0] - 2026-06-08

Added

  • CSS/SCSS taint now bridges into JS imports for the "JS-bundled CSS" pattern (most libs/gdc-*). Previously, a changed SCSS file propagated through cross-library @use chains (e.g. gdc-dashboards-runtime/src/styles/app.scss @uses @gooddata/sdk-ui-dashboard) but the taint lived in a separate __css__: namespace that was only matched against style imports. A consumer that pulls the styles in purely via a JavaScript import — import { Root } from "gdc-dashboards-runtime", where Root.tsx does `import "./styles/app.scs...
Read more

Release v0.24.13

Choose a tag to compare

@github-actions github-actions released this 01 Aug 19:09
ccfb6bd

Docker Image

The Docker image for this release has been published to DockerHub:

Repository: gooddata/gooddata-goodchanges

Tags:

  • gooddata/gooddata-goodchanges:0.24.13
  • gooddata/gooddata-goodchanges:latest

Pull Commands

# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.24.13

# Pull latest
docker pull gooddata/gooddata-goodchanges:latest

Run Command

docker run --rm gooddata/gooddata-goodchanges:0.24.13 [command]

Standalone Binaries

Download the binary for your platform from the assets below.

Platform Architecture Asset
Linux x86_64 goodchanges-linux-amd64.tar.gz
Linux ARM64 goodchanges-linux-arm64.tar.gz
macOS Intel goodchanges-darwin-amd64.tar.gz
macOS Apple Silicon goodchanges-darwin-arm64.tar.gz
Windows x86_64 goodchanges-windows-amd64.zip
Windows ARM64 goodchanges-windows-arm64.zip
All platforms
  • goodchanges-darwin-amd64.tar.gz
  • goodchanges-darwin-amd64.tar.gz.sha256
  • goodchanges-darwin-arm64.tar.gz
  • goodchanges-darwin-arm64.tar.gz.sha256
  • goodchanges-linux-amd64.tar.gz
  • goodchanges-linux-amd64.tar.gz.sha256
  • goodchanges-linux-arm64.tar.gz
  • goodchanges-linux-arm64.tar.gz.sha256
  • goodchanges-windows-amd64.zip
  • goodchanges-windows-amd64.zip.sha256
  • goodchanges-windows-arm64.zip
  • goodchanges-windows-arm64.zip.sha256

Install (Linux/macOS)

# Example: download and install linux/amd64
curl -sL https://github.com/gooddata/gooddata-goodchanges/releases/download/v0.24.13/goodchanges-linux-amd64.tar.gz | tar xz
chmod +x goodchanges-linux-amd64
sudo mv goodchanges-linux-amd64 /usr/local/bin/goodchanges

Changelog

[0.24.13] - 2026-08-01

Fixed

  • Destructuring variable declarations (const { a, b } = expr / const [a, b] = expr) are now decomposed into their individual bindings for both symbol and export extraction. Previously getDeclName only handled plain identifiers and returned "" for binding patterns, so destructured names were registered as neither symbols nor exports — they simply didn't exist in the file model. That silently broke taint propagation through a very common pattern: e.g. export const { store, startSagas, … } = createStore() in gdc-analytical-designer-runtime's reduxStore.ts. A runtime change to the reducers feeding createStore tainted createStore, but the taint could not reach store/startSagas/…, so the package reported 0 affected exports and nothing propagated to the AD module/harness (or the dashboards harness that embeds it) — a false negative. Each destructured binding is now emitted as a symbol/export; renames ({ a: b } → b), rest elements (...x), nested patterns and array holes are handled. To stay precise, a binding's symbol span is the initializer expression (not the whole statement), so the sibling binding names don't bleed into the compared body and cross-link in the AST diff — each binding is attributed exactly to the initializer it destructures.

[0.24.12] - 2026-07-30

Fixed

  • App taint propagation (added in 0.24.1) no longer over-flags downstream targets. An affected app was seeded into the upstream-taint map — tainting everything that imports it — whenever it was merely reachable in the affected set, i.e. a transitive dependent of any touched package. So a change to a library an app depends on flagged that app's harness/host consumers even when the change affected no export the app actually imports (e.g. removing unused exports from gdc-analytical-designer-runtime flagged gdc-analytical-designer-harness, gdc-dashboards-harness, and gdc-host-application). The wholesale-taint seed is now gated on the app being genuinely affected — the same conditions its own target detection uses: directly changed (has changed files), a changed lockfile dependency, or an actual tainted import from upstream (HasTaintedImportsForGlob). Apps that are only transitively reachable but import nothing that changed no longer propagate taint; real changes to exports an app imports still propagate as before. The per-package upstream-taint filter is also factored into a shared buildPkgUpstreamTaint helper used by both the library and app paths.

[0.24.11] - 2026-07-30

Changed

[0.24.10] - 2026-07-30

Fixed

  • Symbol-level AST diff no longer marks an unchanged declaration as changed when a neighboring declaration is edited or removed. Each symbol's body text was extracted starting at stmt.Pos(), which in the TS AST includes the symbol's leading trivia (the preceding comments and blank lines). Deleting a sibling — or otherwise changing what comes before a symbol — re-attaches the intervening comment (e.g. a // SECTION header) to the next symbol, so its extracted body differed between the old and new versions and it was reported as a runtime change. That false "change" then spread through the intra-file reference graph and out via the library's exports, over-tainting downstream consumers. Symbol start lines are now taken from the first real token (scanner.SkipTrivia), excluding leading comments/blank lines from the compared body. Example: removing three unused createSelector exports from bootstrap_selector.ts previously tainted nine untouched sibling selectors (and everything reachable from them); it now taints none.

[0.24.9] - 2026-07-30

Changed

  • Renamed the LOG_LEVEL=BASIC/DEBUG summary line Affected e2e packages (N): to Affected targets (N):. The list has always contained resolved .goodchangesrc.json targets (unit-test targets, app/harness targets, etc.), not just e2e packages, so the label now reflects its actual contents. Log wording only — the JSON result on stdout is unchanged.

[0.24.8] - 2026-07-23

Changed

[0.24.7] - 2026-07-23

Changed

[0.24.6] - 2026-07-23

Changed

[0.24.5] - 2026-07-23

Changed

[0.24.4] - 2026-07-23

Changed

[0.24.3] - 2026-07-23

Changed

[0.24.2] - 2026-07-23

Changed

[0.24.1] - 2026-07-21

Fixed

  • Affected apps now propagate taint to packages that import them. When a package is not a library, its per-symbol export analysis is skipped — but it was also seeding no taint at all, so the taint chain died at the app boundary. A consumer importing from an affected app (e.g. a thin harness that dynamically imports its app package: () => import("gdc-analytical-designer-module")) saw a clean upstream-taint map and was never flagged, so app/harness targets sitting behind an intermediate app went undetected. Now an affected app is tainted wholesale: all of its entrypoint exports are seeded into the upstream-taint map (mirroring the existing global-changeDirs full-taint seeding for libraries), so downstream importers match via the normal import graph — including bare/dynamic side-effect imports (empty import names), which match on any non-empty symbol set for the package. Example: a change in gdc-analytical-designer-runtime now correctly reaches gdc-analytical-designer-harness through the intermediate gdc-analytical-designer-module app.

[0.24.0] - 2026-06-08

Added

  • CSS/SCSS taint now bridges into JS imports for the "JS-bundled CSS" pattern (most libs/gdc-*). Previously, a changed SCSS file propagated through cross-library @use chains (e.g. gdc-dashboards-runtime/src/styles/app.scss @uses @gooddata/sdk-ui-dashboard) but the taint lived in a separate __css__: namespace that was only matched against style imports. A consumer that pulls the styles in purely via a JavaScript import — import { Root } from "gdc-dashboards-runtime", where Root.tsx does import "./styles/app.scss" — never matched, so prod-affecting CSS changes failed to trigger app targets like gdc-dashboards. Now, while analysing each library (with INCLUDE_CSS=1), a local style file is treated as tainted if it @uses the styles of a CSS-tainted upstream package; any TS file that side-effect-imports that style file inherits taint on its exported symbols, which then rides the normal TS import graph into JS consumers. The __css__ closure is computed before library analysis (and threaded through the per-package upstream-taint filter) so it is available during seeding. This implements Stage 3 of properly-support-tree-shaken-scss-or-scss-modules.md; package.json sideEffects gating is still to come.

[0.23.0] - 2026-06-07

Removed

  • Breaking: The app field on target definitions in .goodchangesrc.json (the "app-relationship" feature) and its IGNORE_APP_RELATIONSHIP env var are removed. Targets are no longer triggered just because a referenced app is affected — linking e2e targe...
Read more