Skip to content

Commit 6b5a38b

Browse files
cotticursoragent
andauthored
changelog(prepare-artifact): treat empty existing-filename as unset (#3314)
CLI parsers (Argh, since #3202) forward `--existing-changelog-filename ""` as the empty string instead of null, so the existing `!= null` guard let empty filenames through. `Path.GetFileName("")` returns `""`, then `Path.Combine(OutputDir, "")` collapses to `OutputDir` itself, and the artifact YAML write target becomes the directory path. On Linux this fails with EACCES: Unhandled ChangelogPrepareArtifactService exception: Access to the path '.../.artifacts/changelog-artifact' is denied. System.IO.IOException: Permission denied at System.IO.File.OpenHandle(...) at System.IO.File.<WriteToFileAsync>... Switch the guard to `!string.IsNullOrEmpty` and add a regression test. A complementary fix in elastic/docs-actions stops forwarding the flag when the value is empty. Repro: elastic/cloud PR #154858 / run 25721575364 Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 549596d commit 6b5a38b

2 files changed

Lines changed: 29 additions & 3 deletions

File tree

src/services/Elastic.Changelog/Evaluation/ChangelogPrepareArtifactService.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,17 @@ public async Task<bool> PrepareArtifact(IDiagnosticsCollector collector, Prepare
4747

4848
if (sourceYaml != null)
4949
{
50-
changelogFilename = input.ExistingChangelogFilename != null
51-
? _fileSystem.Path.GetFileName(input.ExistingChangelogFilename)
50+
// Treat empty as unset: CLI parsers (e.g. Argh) forward `--flag ""`
51+
// as the empty string instead of null. An empty filename here would
52+
// make Path.Combine(OutputDir, "") collapse to OutputDir itself and
53+
// turn the artifact write into a write against the directory path,
54+
// which fails with EACCES on Linux.
55+
var hasExistingFilename = !string.IsNullOrEmpty(input.ExistingChangelogFilename);
56+
changelogFilename = hasExistingFilename
57+
? _fileSystem.Path.GetFileName(input.ExistingChangelogFilename!)
5258
: _fileSystem.Path.GetFileName(sourceYaml);
5359

54-
if (input.ExistingChangelogFilename != null)
60+
if (hasExistingFilename)
5561
_logger.LogInformation("Reusing existing filename {Filename} for stable path on branch", changelogFilename);
5662

5763
var destYaml = _fileSystem.Path.Combine(input.OutputDir, changelogFilename);

tests/Elastic.Changelog.Tests/Evaluation/ChangelogPrepareArtifactServiceTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,26 @@ public async Task PrepareArtifact_ExistingFilename_RenamesStagingFileToMatch()
232232
metadata.ChangelogFilename.Should().Be("1735689600-old-title.yaml");
233233
}
234234

235+
[Fact]
236+
public async Task PrepareArtifact_EmptyExistingFilename_FallsBackToStagingFilename()
237+
{
238+
// Regression: CLI parsers (Argh) forward `--existing-changelog-filename ""`
239+
// as the empty string instead of null. An empty filename used to make
240+
// Path.Combine(OutputDir, "") collapse to OutputDir itself and write the
241+
// artifact YAML at the directory path → EACCES on Linux.
242+
await SetupStagingYaml("1735700000-new-title.yaml");
243+
await SetupConfig();
244+
var service = CreateService();
245+
var args = DefaultArgs() with { ExistingChangelogFilename = string.Empty };
246+
247+
var result = await service.PrepareArtifact(Collector, args, CancellationToken.None);
248+
249+
result.Should().BeTrue();
250+
FileSystem.File.Exists(Path.Join(OutputDir, "1735700000-new-title.yaml")).Should().BeTrue();
251+
var metadata = ReadMetadata();
252+
metadata.ChangelogFilename.Should().Be("1735700000-new-title.yaml");
253+
}
254+
235255
[Fact]
236256
public async Task PrepareArtifact_MissingStagingYaml_StatusError()
237257
{

0 commit comments

Comments
 (0)