Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ namespace Elastic.Documentation.FileSystems;

/// <summary>
/// Scope for changelog commands: the git root of the target repository.
/// Allows reading <c>.git</c> metadata (remote URL, branch); does not include
/// AppData or build artifacts — changelog operates only within the repo working tree.
/// Allows reading <c>.git</c> metadata (remote URL, branch) and writing under
/// the conventional <c>.artifacts</c> CI staging directory. Does not include
/// AppData — changelog operates only within the repo working tree.
/// </summary>
public class ChangelogFileSystem(IDirectoryInfo root, IFileSystem? inner = null)
: ScopedFileSystem(inner ?? Physical, new ScopedFileSystemOptions([root.FullName])
{
AllowedHiddenFolderNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".git" },
AllowedHiddenFolderNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".git", ".artifacts" },
AllowedHiddenFileNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".git" }
}),
IChangelogFileSystem
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System.IO.Abstractions.TestingHelpers;
using AwesomeAssertions;
using Elastic.Documentation.Configuration;
using Elastic.Documentation.FileSystems;
using Nullean.ScopedFileSystem;

namespace Elastic.Changelog.Tests.Evaluation;

/// <summary>
/// Regression: changelog CI staging uses <c>.artifacts/changelog-*</c> under the checkout.
/// ScopedFileSystem rejects hidden path segments unless they are allowlisted.
/// </summary>
public class ChangelogCiFileSystemTests
{
private static readonly string Root = Paths.WorkingDirectoryRoot.FullName;

[Fact]
public void ChangelogFileSystem_AllowsArtifactsStagingAndArtifactDirs()
{
var mock = new MockFileSystem(new MockFileSystemOptions { CurrentDirectory = Root });
var fs = ChangelogFileSystem.FromWorkingDirectory(mock);

var staging = Path.Join(Root, ".artifacts", "changelog-staging");
var artifact = Path.Join(Root, ".artifacts", "changelog-artifact");
var stagingFile = Path.Join(staging, "42.yaml");
var artifactFile = Path.Join(artifact, "metadata.json");

var create = () =>
{
fs.Directory.CreateDirectory(staging);
fs.Directory.CreateDirectory(artifact);
fs.File.WriteAllText(stagingFile, "title: test");
fs.File.WriteAllText(artifactFile, "{}");
};

create.Should().NotThrow();
fs.File.Exists(stagingFile).Should().BeTrue();
fs.File.Exists(artifactFile).Should().BeTrue();
}

[Fact]
public void ChangelogFileSystem_BlocksOtherHiddenDirectories()
{
var mock = new MockFileSystem(new MockFileSystemOptions { CurrentDirectory = Root });
var fs = ChangelogFileSystem.FromWorkingDirectory(mock);
var hidden = Path.Join(Root, ".hidden", "nested");

var create = () => fs.Directory.CreateDirectory(hidden);

create.Should().Throw<ScopedFileSystemException>()
.WithMessage("*hidden*");
}

[Fact]
public void RunnerTempFileSystem_BlocksOtherHiddenDirectories()
{
var mock = new MockFileSystem(new MockFileSystemOptions { CurrentDirectory = Root });
var fs = new RunnerTempFileSystem(mock.DirectoryInfo.New(Root), inner: mock);
var hidden = Path.Join(Root, ".hidden", "nested");

var create = () => fs.Directory.CreateDirectory(hidden);

create.Should().Throw<ScopedFileSystemException>()
.WithMessage("*hidden*");
}
}
Loading