Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
20 changes: 17 additions & 3 deletions src/NuGetizer.Tasks/CreatePackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -237,16 +237,30 @@ void GeneratePackage(Stream output = null)
Uri.TryCreate(manifest.Metadata.Repository.Url, UriKind.Absolute, out var uri) &&
uri.Host.EndsWith("github.com"))
{
// expr to match markdown links. use named groups to capture the link text and url.
linkExpr ??= new Regex(@"\[(?<text>[^\]]+)\]\((?<url>[^)]+)\)", RegexOptions.None);
// expr to match markdown links with optional title. use named groups to capture the link text, url and optional title.
linkExpr ??= new Regex(@"\[(?<text>[^\]]+)\]\((?<url>[^\s)]+)(?:\s+""(?<title>[^""]*)"")?\)", RegexOptions.None);
var repoUrl = manifest.Metadata.Repository.Url.TrimEnd('/');

// Extract owner and repo from URL for raw.githubusercontent.com format
var repoPath = uri.AbsolutePath.TrimStart('/');
var rawBaseUrl = $"https://raw.githubusercontent.com/{repoPath}";

replaced = linkExpr.Replace(replaced, match =>
{
var url = match.Groups["url"].Value;
var title = match.Groups["title"].Value;

// Check if the URL is already absolute
if (Uri.IsWellFormedUriString(url, UriKind.Absolute))
return match.Value;

var newUrl = $"{repoUrl}/blob/{manifest.Metadata.Repository.Commit}/{url.TrimStart('/')}";
// Use raw.githubusercontent.com format for proper image display on nuget.org
var newUrl = $"{rawBaseUrl}/{manifest.Metadata.Repository.Commit}/{url.TrimStart('/')}";

// Preserve the title if present
if (!string.IsNullOrEmpty(title))
return $"[{match.Groups["text"].Value}]({newUrl} \"{title}\")";

return $"[{match.Groups["text"].Value}]({newUrl})";
});
}
Expand Down
109 changes: 108 additions & 1 deletion src/NuGetizer.Tests/CreatePackageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,114 @@ public void when_readme_has_relativeurl_then_expands_github_url()

var readme = File.ReadAllText(file.Source);

Assert.Contains("[license](https://github.com/devlooped/nugetizer/blob/9dc2cb5de/license.txt)", readme);
Assert.Contains("[license](https://raw.githubusercontent.com/devlooped/nugetizer/9dc2cb5de/license.txt)", readme);
}

[Fact]
public void when_readme_has_link_with_tooltip_then_preserves_tooltip()
{
var content = Path.GetTempFileName();
File.WriteAllText(content, "See ![avatar](avatars/user.png \"User Avatar\").");
task.Contents = new[]
{
new TaskItem(content, new Metadata
{
{ MetadataName.PackageId, task.Manifest.GetMetadata("Id") },
{ MetadataName.PackFolder, PackFolderKind.None },
{ MetadataName.PackagePath, "readme.md" }
}),
};

task.Manifest.SetMetadata("Readme", "readme.md");
task.Manifest.SetMetadata("RepositoryType", "git");
task.Manifest.SetMetadata("RepositoryUrl", "https://github.com/devlooped/nugetizer");
task.Manifest.SetMetadata("RepositorySha", "abc123def");

createPackage = true;
ExecuteTask(out var manifest);

Assert.NotNull(manifest);

var file = manifest.Files.FirstOrDefault(f => Path.GetFileName(f.Target) == manifest.Metadata.Readme);
Assert.NotNull(file);
Assert.True(File.Exists(file.Source));

var readme = File.ReadAllText(file.Source);

Assert.Contains("[avatar](https://raw.githubusercontent.com/devlooped/nugetizer/abc123def/avatars/user.png \"User Avatar\")", readme);
}

[Fact]
public void when_readme_has_absolute_url_then_does_not_replace()
{
var content = Path.GetTempFileName();
File.WriteAllText(content, "[![badge](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/user.png \"User\")](https://github.com/user)");
task.Contents = new[]
{
new TaskItem(content, new Metadata
{
{ MetadataName.PackageId, task.Manifest.GetMetadata("Id") },
{ MetadataName.PackFolder, PackFolderKind.None },
{ MetadataName.PackagePath, "readme.md" }
}),
};

task.Manifest.SetMetadata("Readme", "readme.md");
task.Manifest.SetMetadata("RepositoryType", "git");
task.Manifest.SetMetadata("RepositoryUrl", "https://github.com/devlooped/nugetizer");
task.Manifest.SetMetadata("RepositorySha", "abc123def");

createPackage = true;
ExecuteTask(out var manifest);

Assert.NotNull(manifest);

var file = manifest.Files.FirstOrDefault(f => Path.GetFileName(f.Target) == manifest.Metadata.Readme);
Assert.NotNull(file);
Assert.True(File.Exists(file.Source));

var readme = File.ReadAllText(file.Source);

// Should NOT prepend repository URL to absolute URLs
Assert.DoesNotContain("https://raw.githubusercontent.com/devlooped/nugetizer/abc123def/https://raw.githubusercontent.com", readme);
// Should preserve the original absolute URL
Assert.Contains("https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/user.png", readme);
}

[Fact]
public void when_readme_has_image_link_then_uses_raw_url()
{
var content = Path.GetTempFileName();
File.WriteAllText(content, "![Image](img/logo.png)");
task.Contents = new[]
{
new TaskItem(content, new Metadata
{
{ MetadataName.PackageId, task.Manifest.GetMetadata("Id") },
{ MetadataName.PackFolder, PackFolderKind.None },
{ MetadataName.PackagePath, "readme.md" }
}),
};

task.Manifest.SetMetadata("Readme", "readme.md");
task.Manifest.SetMetadata("RepositoryType", "git");
task.Manifest.SetMetadata("RepositoryUrl", "https://github.com/devlooped/nugetizer");
task.Manifest.SetMetadata("RepositorySha", "abc123def");

createPackage = true;
ExecuteTask(out var manifest);

Assert.NotNull(manifest);

var file = manifest.Files.FirstOrDefault(f => Path.GetFileName(f.Target) == manifest.Metadata.Readme);
Assert.NotNull(file);
Assert.True(File.Exists(file.Source));

var readme = File.ReadAllText(file.Source);

// Should use raw.githubusercontent.com format for proper image display
Assert.Contains("https://raw.githubusercontent.com/devlooped/nugetizer/abc123def/img/logo.png", readme);
Assert.DoesNotContain("/blob/", readme);
}

[Fact]
Expand Down
Loading