Releases: gooddata/gooddata-goodchanges
Release list
Release v0.25.8
Docker Image
The Docker image for this release has been published to DockerHub:
Repository: gooddata/gooddata-goodchanges
Tags:
gooddata/gooddata-goodchanges:0.25.8gooddata/gooddata-goodchanges:latest
Pull Commands
# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.8
# Pull latest
docker pull gooddata/gooddata-goodchanges:latestRun 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.gzgoodchanges-darwin-amd64.tar.gz.sha256goodchanges-darwin-arm64.tar.gzgoodchanges-darwin-arm64.tar.gz.sha256goodchanges-linux-amd64.tar.gzgoodchanges-linux-amd64.tar.gz.sha256goodchanges-linux-arm64.tar.gzgoodchanges-linux-arm64.tar.gz.sha256goodchanges-windows-amd64.zipgoodchanges-windows-amd64.zip.sha256goodchanges-windows-arm64.zipgoodchanges-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/goodchangesChangelog
[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"andexport type { T } from "./x"are erased at compile time and never load./xat 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 ontsparse.Importsince 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, soexport type { T } from "./x"followed byexport { f } from "./x"still counts as a runtime connection). Ordinary named-symbol taint through type-only edges is unchanged (relevant forINCLUDE_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-leveltest()/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.deleteLLMProviderfrom"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 ownhelpers/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/bareImportsChangedchecks 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 bareimport "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 bareimport "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 entrypointindex.ts) only picked up the re-exported symbols, so consumers importing the barrel for other symbols were missed — e.g. aconsole.logadded toe2e-utils/src/api/ai/ai.tsflagged only the 5 consumers of theaiexports instead of all 8 e2e-utils consumers (importing the barrel loadsai.tsand 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; aTODOin the propagation notes the follow-up to refine it using each package'spackage.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 unusedchooseActiontainted the surviving, usedchooseActionByIndex(its body "contains" the stringchooseAction), which then propagated to a spec and flaggedgdc-dashboards-e2efor a dead-code deletion. All six propagation sites (astdiff.gointra-file graph, plus the seed/importer/usage propagation inanalyzer.go) now require the name to appear flanked by non-identifier characters ([A-Za-z0-9_$]) via a sharedcontainsIdentifierhelper; 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,FindAffectedFilestainted 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 viahasSideEffectStmtChanges; - a bare side-effect import (
import "./x") added, removed, or re-pointed.
- a top-level side-effect statement (
- 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, whenincludeTypesis off) correctly taint nothing. Import statement type-only-ness is now tracked ontsparse.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_MESSAGEfromgdc-ldm-modeler-e2e'splaywright/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...
Release v0.25.7
Docker Image
The Docker image for this release has been published to DockerHub:
Repository: gooddata/gooddata-goodchanges
Tags:
gooddata/gooddata-goodchanges:0.25.7gooddata/gooddata-goodchanges:latest
Pull Commands
# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.7
# Pull latest
docker pull gooddata/gooddata-goodchanges:latestRun 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.gzgoodchanges-darwin-amd64.tar.gz.sha256goodchanges-darwin-arm64.tar.gzgoodchanges-darwin-arm64.tar.gz.sha256goodchanges-linux-amd64.tar.gzgoodchanges-linux-amd64.tar.gz.sha256goodchanges-linux-arm64.tar.gzgoodchanges-linux-arm64.tar.gz.sha256goodchanges-windows-amd64.zipgoodchanges-windows-amd64.zip.sha256goodchanges-windows-arm64.zipgoodchanges-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/goodchangesChangelog
[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-leveltest()/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.deleteLLMProviderfrom"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 ownhelpers/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/bareImportsChangedchecks 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 bareimport "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 bareimport "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 entrypointindex.ts) only picked up the re-exported symbols, so consumers importing the barrel for other symbols were missed — e.g. aconsole.logadded toe2e-utils/src/api/ai/ai.tsflagged only the 5 consumers of theaiexports instead of all 8 e2e-utils consumers (importing the barrel loadsai.tsand 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; aTODOin the propagation notes the follow-up to refine it using each package'spackage.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 unusedchooseActiontainted the surviving, usedchooseActionByIndex(its body "contains" the stringchooseAction), which then propagated to a spec and flaggedgdc-dashboards-e2efor a dead-code deletion. All six propagation sites (astdiff.gointra-file graph, plus the seed/importer/usage propagation inanalyzer.go) now require the name to appear flanked by non-identifier characters ([A-Za-z0-9_$]) via a sharedcontainsIdentifierhelper; 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,FindAffectedFilestainted 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 viahasSideEffectStmtChanges; - a bare side-effect import (
import "./x") added, removed, or re-pointed.
- a top-level side-effect statement (
- 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, whenincludeTypesis off) correctly taint nothing. Import statement type-only-ness is now tracked ontsparse.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_MESSAGEfromgdc-ldm-modeler-e2e'splaywright/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 isundefined— 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 escapedfindTaintedSymbolsByUsage— 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,...
Release v0.25.6
Docker Image
The Docker image for this release has been published to DockerHub:
Repository: gooddata/gooddata-goodchanges
Tags:
gooddata/gooddata-goodchanges:0.25.6gooddata/gooddata-goodchanges:latest
Pull Commands
# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.6
# Pull latest
docker pull gooddata/gooddata-goodchanges:latestRun 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.gzgoodchanges-darwin-amd64.tar.gz.sha256goodchanges-darwin-arm64.tar.gzgoodchanges-darwin-arm64.tar.gz.sha256goodchanges-linux-amd64.tar.gzgoodchanges-linux-amd64.tar.gz.sha256goodchanges-linux-arm64.tar.gzgoodchanges-linux-arm64.tar.gz.sha256goodchanges-windows-amd64.zipgoodchanges-windows-amd64.zip.sha256goodchanges-windows-arm64.zipgoodchanges-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/goodchangesChangelog
[0.25.6] - 2026-08-11
Fixed
- Import-time side effects are now detected even when the same diff also changes symbols. The
hasSideEffectStmtChanges/bareImportsChangedchecks 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 bareimport "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 bareimport "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 entrypointindex.ts) only picked up the re-exported symbols, so consumers importing the barrel for other symbols were missed — e.g. aconsole.logadded toe2e-utils/src/api/ai/ai.tsflagged only the 5 consumers of theaiexports instead of all 8 e2e-utils consumers (importing the barrel loadsai.tsand 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; aTODOin the propagation notes the follow-up to refine it using each package'spackage.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 unusedchooseActiontainted the surviving, usedchooseActionByIndex(its body "contains" the stringchooseAction), which then propagated to a spec and flaggedgdc-dashboards-e2efor a dead-code deletion. All six propagation sites (astdiff.gointra-file graph, plus the seed/importer/usage propagation inanalyzer.go) now require the name to appear flanked by non-identifier characters ([A-Za-z0-9_$]) via a sharedcontainsIdentifierhelper; 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,FindAffectedFilestainted 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 viahasSideEffectStmtChanges; - a bare side-effect import (
import "./x") added, removed, or re-pointed.
- a top-level side-effect statement (
- 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, whenincludeTypesis off) correctly taint nothing. Import statement type-only-ness is now tracked ontsparse.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_MESSAGEfromgdc-ldm-modeler-e2e'splaywright/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 isundefined— 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 escapedfindTaintedSymbolsByUsage— 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()](orconst { 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:adepends ontainted()andbonsafe(), so a change tosafe()no longer flags consumers ofa.
[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. PreviouslygetDeclNameonly 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...
Release v0.25.5
Docker Image
The Docker image for this release has been published to DockerHub:
Repository: gooddata/gooddata-goodchanges
Tags:
gooddata/gooddata-goodchanges:0.25.5gooddata/gooddata-goodchanges:latest
Pull Commands
# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.5
# Pull latest
docker pull gooddata/gooddata-goodchanges:latestRun 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.gzgoodchanges-darwin-amd64.tar.gz.sha256goodchanges-darwin-arm64.tar.gzgoodchanges-darwin-arm64.tar.gz.sha256goodchanges-linux-amd64.tar.gzgoodchanges-linux-amd64.tar.gz.sha256goodchanges-linux-arm64.tar.gzgoodchanges-linux-arm64.tar.gz.sha256goodchanges-windows-amd64.zipgoodchanges-windows-amd64.zip.sha256goodchanges-windows-arm64.zipgoodchanges-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/goodchangesChangelog
[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 bareimport "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 entrypointindex.ts) only picked up the re-exported symbols, so consumers importing the barrel for other symbols were missed — e.g. aconsole.logadded toe2e-utils/src/api/ai/ai.tsflagged only the 5 consumers of theaiexports instead of all 8 e2e-utils consumers (importing the barrel loadsai.tsand 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; aTODOin the propagation notes the follow-up to refine it using each package'spackage.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 unusedchooseActiontainted the surviving, usedchooseActionByIndex(its body "contains" the stringchooseAction), which then propagated to a spec and flaggedgdc-dashboards-e2efor a dead-code deletion. All six propagation sites (astdiff.gointra-file graph, plus the seed/importer/usage propagation inanalyzer.go) now require the name to appear flanked by non-identifier characters ([A-Za-z0-9_$]) via a sharedcontainsIdentifierhelper; 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,FindAffectedFilestainted 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 viahasSideEffectStmtChanges; - a bare side-effect import (
import "./x") added, removed, or re-pointed.
- a top-level side-effect statement (
- 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, whenincludeTypesis off) correctly taint nothing. Import statement type-only-ness is now tracked ontsparse.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_MESSAGEfromgdc-ldm-modeler-e2e'splaywright/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 isundefined— 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 escapedfindTaintedSymbolsByUsage— 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()](orconst { 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:adepends ontainted()andbonsafe(), so a change tosafe()no longer flags consumers ofa.
[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. PreviouslygetDeclNameonly 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()ingdc-analytical-designer-runtime'sreduxStore.ts. A runtime change to the reducers feedingcreateStoretaintedcreateStore, but the taint could not reachstore/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...
Release v0.25.4
Docker Image
The Docker image for this release has been published to DockerHub:
Repository: gooddata/gooddata-goodchanges
Tags:
gooddata/gooddata-goodchanges:0.25.4gooddata/gooddata-goodchanges:latest
Pull Commands
# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.4
# Pull latest
docker pull gooddata/gooddata-goodchanges:latestRun 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.gzgoodchanges-darwin-amd64.tar.gz.sha256goodchanges-darwin-arm64.tar.gzgoodchanges-darwin-arm64.tar.gz.sha256goodchanges-linux-amd64.tar.gzgoodchanges-linux-amd64.tar.gz.sha256goodchanges-linux-arm64.tar.gzgoodchanges-linux-arm64.tar.gz.sha256goodchanges-windows-amd64.zipgoodchanges-windows-amd64.zip.sha256goodchanges-windows-arm64.zipgoodchanges-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/goodchangesChangelog
[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 unusedchooseActiontainted the surviving, usedchooseActionByIndex(its body "contains" the stringchooseAction), which then propagated to a spec and flaggedgdc-dashboards-e2efor a dead-code deletion. All six propagation sites (astdiff.gointra-file graph, plus the seed/importer/usage propagation inanalyzer.go) now require the name to appear flanked by non-identifier characters ([A-Za-z0-9_$]) via a sharedcontainsIdentifierhelper; 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,FindAffectedFilestainted 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 viahasSideEffectStmtChanges; - a bare side-effect import (
import "./x") added, removed, or re-pointed.
- a top-level side-effect statement (
- 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, whenincludeTypesis off) correctly taint nothing. Import statement type-only-ness is now tracked ontsparse.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_MESSAGEfromgdc-ldm-modeler-e2e'splaywright/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 isundefined— 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 escapedfindTaintedSymbolsByUsage— 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()](orconst { 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:adepends ontainted()andbonsafe(), so a change tosafe()no longer flags consumers ofa.
[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. PreviouslygetDeclNameonly 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()ingdc-analytical-designer-runtime'sreduxStore.ts. A runtime change to the reducers feedingcreateStoretaintedcreateStore, but the taint could not reachstore/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-runtimeflaggedgdc-analytical-designer-harness,gdc-dashboards-harness, andgdc-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 sharedbuildPkgUpstreamTainthelper used by both the library and app paths.
[0.24.11] - 2026-07-30
Changed
- Upgrade vendored typescript-go to
37357ae666e7
[0.24.10] - 2026-07-30
Fixed
- Symbol-level AST diff no longer marks an unchanged declaration as changed when ...
Release v0.25.3
Docker Image
The Docker image for this release has been published to DockerHub:
Repository: gooddata/gooddata-goodchanges
Tags:
gooddata/gooddata-goodchanges:0.25.3gooddata/gooddata-goodchanges:latest
Pull Commands
# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.3
# Pull latest
docker pull gooddata/gooddata-goodchanges:latestRun 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.gzgoodchanges-darwin-amd64.tar.gz.sha256goodchanges-darwin-arm64.tar.gzgoodchanges-darwin-arm64.tar.gz.sha256goodchanges-linux-amd64.tar.gzgoodchanges-linux-amd64.tar.gz.sha256goodchanges-linux-arm64.tar.gzgoodchanges-linux-arm64.tar.gz.sha256goodchanges-windows-amd64.zipgoodchanges-windows-amd64.zip.sha256goodchanges-windows-arm64.zipgoodchanges-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/goodchangesChangelog
[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,FindAffectedFilestainted 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 viahasSideEffectStmtChanges; - a bare side-effect import (
import "./x") added, removed, or re-pointed.
- a top-level side-effect statement (
- 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, whenincludeTypesis off) correctly taint nothing. Import statement type-only-ness is now tracked ontsparse.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_MESSAGEfromgdc-ldm-modeler-e2e'splaywright/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 isundefined— 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 escapedfindTaintedSymbolsByUsage— 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()](orconst { 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:adepends ontainted()andbonsafe(), so a change tosafe()no longer flags consumers ofa.
[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. PreviouslygetDeclNameonly 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()ingdc-analytical-designer-runtime'sreduxStore.ts. A runtime change to the reducers feedingcreateStoretaintedcreateStore, but the taint could not reachstore/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-runtimeflaggedgdc-analytical-designer-harness,gdc-dashboards-harness, andgdc-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 sharedbuildPkgUpstreamTainthelper used by both the library and app paths.
[0.24.11] - 2026-07-30
Changed
- Upgrade vendored typescript-go to
37357ae666e7
[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// SECTIONheader) 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 unusedcreateSelectorexports frombootstrap_selector.tspreviously tainted nine untouched sibling selectors (and everything reachable from them); it now taints none.
[0...
Release v0.25.2
Docker Image
The Docker image for this release has been published to DockerHub:
Repository: gooddata/gooddata-goodchanges
Tags:
gooddata/gooddata-goodchanges:0.25.2gooddata/gooddata-goodchanges:latest
Pull Commands
# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.2
# Pull latest
docker pull gooddata/gooddata-goodchanges:latestRun 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.gzgoodchanges-darwin-amd64.tar.gz.sha256goodchanges-darwin-arm64.tar.gzgoodchanges-darwin-arm64.tar.gz.sha256goodchanges-linux-amd64.tar.gzgoodchanges-linux-amd64.tar.gz.sha256goodchanges-linux-arm64.tar.gzgoodchanges-linux-arm64.tar.gz.sha256goodchanges-windows-amd64.zipgoodchanges-windows-amd64.zip.sha256goodchanges-windows-arm64.zipgoodchanges-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/goodchangesChangelog
[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_MESSAGEfromgdc-ldm-modeler-e2e'splaywright/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 isundefined— 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 escapedfindTaintedSymbolsByUsage— 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()](orconst { 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:adepends ontainted()andbonsafe(), so a change tosafe()no longer flags consumers ofa.
[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. PreviouslygetDeclNameonly 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()ingdc-analytical-designer-runtime'sreduxStore.ts. A runtime change to the reducers feedingcreateStoretaintedcreateStore, but the taint could not reachstore/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-runtimeflaggedgdc-analytical-designer-harness,gdc-dashboards-harness, andgdc-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 sharedbuildPkgUpstreamTainthelper used by both the library and app paths.
[0.24.11] - 2026-07-30
Changed
- Upgrade vendored typescript-go to
37357ae666e7
[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// SECTIONheader) 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 unusedcreateSelectorexports frombootstrap_selector.tspreviously 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/DEBUGsummary lineAffected e2e packages (N):toAffected targets (N):. The list has always contained resolved.goodchangesrc.jsontargets (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
- Upgrade vendored typescript-go to
9696f950a1bb
[0.24.7] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
0d0b387e08e8
[0.24.6] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
8457ea62d288
[0.24.5] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
a7ae3a14e910
[0.24.4] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
375d2b910dc3
[0.24.3] - 2026-07-23
Changed
- Upgrade vendored typescript-go to [
49b85c922aed](microsoft/typescript-go@49b85c922aed1b558676bfb...
Release v0.25.1
Docker Image
The Docker image for this release has been published to DockerHub:
Repository: gooddata/gooddata-goodchanges
Tags:
gooddata/gooddata-goodchanges:0.25.1gooddata/gooddata-goodchanges:latest
Pull Commands
# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.1
# Pull latest
docker pull gooddata/gooddata-goodchanges:latestRun 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.gzgoodchanges-darwin-amd64.tar.gz.sha256goodchanges-darwin-arm64.tar.gzgoodchanges-darwin-arm64.tar.gz.sha256goodchanges-linux-amd64.tar.gzgoodchanges-linux-amd64.tar.gz.sha256goodchanges-linux-arm64.tar.gzgoodchanges-linux-arm64.tar.gz.sha256goodchanges-windows-amd64.zipgoodchanges-windows-amd64.zip.sha256goodchanges-windows-arm64.zipgoodchanges-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/goodchangesChangelog
[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 isundefined— 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 escapedfindTaintedSymbolsByUsage— 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()](orconst { 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:adepends ontainted()andbonsafe(), so a change tosafe()no longer flags consumers ofa.
[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. PreviouslygetDeclNameonly 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()ingdc-analytical-designer-runtime'sreduxStore.ts. A runtime change to the reducers feedingcreateStoretaintedcreateStore, but the taint could not reachstore/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-runtimeflaggedgdc-analytical-designer-harness,gdc-dashboards-harness, andgdc-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 sharedbuildPkgUpstreamTainthelper used by both the library and app paths.
[0.24.11] - 2026-07-30
Changed
- Upgrade vendored typescript-go to
37357ae666e7
[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// SECTIONheader) 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 unusedcreateSelectorexports frombootstrap_selector.tspreviously 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/DEBUGsummary lineAffected e2e packages (N):toAffected targets (N):. The list has always contained resolved.goodchangesrc.jsontargets (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
- Upgrade vendored typescript-go to
9696f950a1bb
[0.24.7] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
0d0b387e08e8
[0.24.6] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
8457ea62d288
[0.24.5] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
a7ae3a14e910
[0.24.4] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
375d2b910dc3
[0.24.3] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
49b85c922aed
[0.24.2] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
0fa4110e4e0e
[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-changeDirsfull-taint seeding for libraries), so downstream importers match via the normal import graph — including bare/dynamic si...
Release v0.25.0
Docker Image
The Docker image for this release has been published to DockerHub:
Repository: gooddata/gooddata-goodchanges
Tags:
gooddata/gooddata-goodchanges:0.25.0gooddata/gooddata-goodchanges:latest
Pull Commands
# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.25.0
# Pull latest
docker pull gooddata/gooddata-goodchanges:latestRun 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.gzgoodchanges-darwin-amd64.tar.gz.sha256goodchanges-darwin-arm64.tar.gzgoodchanges-darwin-arm64.tar.gz.sha256goodchanges-linux-amd64.tar.gzgoodchanges-linux-amd64.tar.gz.sha256goodchanges-linux-arm64.tar.gzgoodchanges-linux-arm64.tar.gz.sha256goodchanges-windows-amd64.zipgoodchanges-windows-amd64.zip.sha256goodchanges-windows-arm64.zipgoodchanges-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/goodchangesChangelog
[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()](orconst { 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:adepends ontainted()andbonsafe(), so a change tosafe()no longer flags consumers ofa.
[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. PreviouslygetDeclNameonly 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()ingdc-analytical-designer-runtime'sreduxStore.ts. A runtime change to the reducers feedingcreateStoretaintedcreateStore, but the taint could not reachstore/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-runtimeflaggedgdc-analytical-designer-harness,gdc-dashboards-harness, andgdc-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 sharedbuildPkgUpstreamTainthelper used by both the library and app paths.
[0.24.11] - 2026-07-30
Changed
- Upgrade vendored typescript-go to
37357ae666e7
[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// SECTIONheader) 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 unusedcreateSelectorexports frombootstrap_selector.tspreviously 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/DEBUGsummary lineAffected e2e packages (N):toAffected targets (N):. The list has always contained resolved.goodchangesrc.jsontargets (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
- Upgrade vendored typescript-go to
9696f950a1bb
[0.24.7] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
0d0b387e08e8
[0.24.6] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
8457ea62d288
[0.24.5] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
a7ae3a14e910
[0.24.4] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
375d2b910dc3
[0.24.3] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
49b85c922aed
[0.24.2] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
0fa4110e4e0e
[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-changeDirsfull-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 ingdc-analytical-designer-runtimenow correctly reachesgdc-analytical-designer-harnessthrough the intermediategdc-analytical-designer-moduleapp.
[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@usechains (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", whereRoot.tsxdoes `import "./styles/app.scs...
Release v0.24.13
Docker Image
The Docker image for this release has been published to DockerHub:
Repository: gooddata/gooddata-goodchanges
Tags:
gooddata/gooddata-goodchanges:0.24.13gooddata/gooddata-goodchanges:latest
Pull Commands
# Pull specific version
docker pull gooddata/gooddata-goodchanges:0.24.13
# Pull latest
docker pull gooddata/gooddata-goodchanges:latestRun 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.gzgoodchanges-darwin-amd64.tar.gz.sha256goodchanges-darwin-arm64.tar.gzgoodchanges-darwin-arm64.tar.gz.sha256goodchanges-linux-amd64.tar.gzgoodchanges-linux-amd64.tar.gz.sha256goodchanges-linux-arm64.tar.gzgoodchanges-linux-arm64.tar.gz.sha256goodchanges-windows-amd64.zipgoodchanges-windows-amd64.zip.sha256goodchanges-windows-arm64.zipgoodchanges-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/goodchangesChangelog
[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. PreviouslygetDeclNameonly 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()ingdc-analytical-designer-runtime'sreduxStore.ts. A runtime change to the reducers feedingcreateStoretaintedcreateStore, but the taint could not reachstore/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-runtimeflaggedgdc-analytical-designer-harness,gdc-dashboards-harness, andgdc-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 sharedbuildPkgUpstreamTainthelper used by both the library and app paths.
[0.24.11] - 2026-07-30
Changed
- Upgrade vendored typescript-go to
37357ae666e7
[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// SECTIONheader) 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 unusedcreateSelectorexports frombootstrap_selector.tspreviously 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/DEBUGsummary lineAffected e2e packages (N):toAffected targets (N):. The list has always contained resolved.goodchangesrc.jsontargets (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
- Upgrade vendored typescript-go to
9696f950a1bb
[0.24.7] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
0d0b387e08e8
[0.24.6] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
8457ea62d288
[0.24.5] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
a7ae3a14e910
[0.24.4] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
375d2b910dc3
[0.24.3] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
49b85c922aed
[0.24.2] - 2026-07-23
Changed
- Upgrade vendored typescript-go to
0fa4110e4e0e
[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-changeDirsfull-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 ingdc-analytical-designer-runtimenow correctly reachesgdc-analytical-designer-harnessthrough the intermediategdc-analytical-designer-moduleapp.
[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@usechains (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", whereRoot.tsxdoesimport "./styles/app.scss"— never matched, so prod-affecting CSS changes failed to trigger app targets likegdc-dashboards. Now, while analysing each library (withINCLUDE_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 ofproperly-support-tree-shaken-scss-or-scss-modules.md;package.jsonsideEffectsgating is still to come.
[0.23.0] - 2026-06-07
Removed
- Breaking: The
appfield on target definitions in.goodchangesrc.json(the "app-relationship" feature) and itsIGNORE_APP_RELATIONSHIPenv var are removed. Targets are no longer triggered just because a referenced app is affected — linking e2e targe...