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
94 changes: 94 additions & 0 deletions .github/workflows/pester-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Pester Tests

on:
pull_request:
branches: [ main ]
paths:
- 'src/**'
- '.github/workflows/pester-tests.yml'
push:
branches: [ main ]
paths:
- 'src/**'
- '.github/workflows/pester-tests.yml'

jobs:
test:
runs-on: windows-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install PowerShell Module Dependencies
shell: pwsh
run: |
Set-PSRepository PSGallery -InstallationPolicy Trusted
Install-Module -Name Pester -Force -SkipPublisherCheck
Install-Module -Name PowerShell-Yaml -Force -SkipPublisherCheck

- name: Run Pester Tests
shell: pwsh
run: |
# Debug: Show current directory and files
Write-Host "Current directory: $(Get-Location)"
Write-Host "Directory structure:"
Get-ChildItem -Path . -Recurse | Select-Object FullName, PSIsContainer

# Change to src directory to match test expectations
Set-Location "./src"
Write-Host "Changed to src directory: $(Get-Location)"

# Configure Pester
$PesterConfig = @{
Run = @{
Path = './tests'
}
Output = @{
Verbosity = 'Detailed'
}
TestResult = @{
Enabled = $true
OutputPath = '../TestResults.xml'
OutputFormat = 'NUnitXml'
}
CodeCoverage = @{
Enabled = $true
Path = './*.ps*1'
OutputPath = '../coverage.xml'
OutputFormat = 'JaCoCo'
}
}

# Run tests
$Results = Invoke-Pester -Configuration $PesterConfig

# Change back to root directory
Set-Location ".."

# Exit with error code if tests failed
if ($Results.FailedCount -gt 0) {
Write-Error "Tests failed: $($Results.FailedCount) failed out of $($Results.TotalCount) total tests"
exit 1
}

Write-Host "All tests passed: $($Results.PassedCount) out of $($Results.TotalCount) tests"

# - name: Publish Test Results
# uses: dorny/test-reporter@v1
# if: always()
# with:
# name: Pester Tests
# path: TestResults.xml
# reporter: java-junit
# fail-on-error: true

# - name: Upload Test Results
# uses: actions/upload-artifact@v4
# if: always()
# with:
# name: test-results
# path: |
# TestResults.xml
# coverage.xml
# retention-days: 7
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,28 @@ A PowerShell library for publishing markdown files authored in markdown to Blogg

## Image Support

PSBlogger supports Google Drive for hosting your images.
PSBlogger supports Google Drive for hosting your images and handles both standard markdown and Obsidian image formats.

### Supported Image Formats

PSBlogger can detect and process images in both formats:

- **Standard Markdown**: `![alt text](image.png "optional title")`
- **Obsidian Format**: `![[image.png|alt text]]`

When publishing, all images are converted to standard markdown format with Google Drive URLs, ensuring compatibility across platforms.

### Google Drive Integration

- Add-GoogleDriveFile: Uploads a file to your Google Drive
- Add-GoogleDriveFolder: Creates a folder in your Google Drive
- Get-GoogleDriveItems: Query the contents of your Google Drive
- Set-GoogleDriveFilePermission: Modifes the permission of a file.

Markdown methods for managing images.
### Markdown Image Management

- Find-MarkdownImages: scans the markdown file to locate images in the content.
- Update-MarkdownImages: updates the content in the markdown file with updated urls
- Find-MarkdownImages: scans the markdown file to locate images in both standard and Obsidian formats
- Update-MarkdownImages: updates the content in the markdown file with updated urls, converting all formats to standard markdown

## Future

Expand Down
62 changes: 53 additions & 9 deletions src/public/Find-MarkdownImages.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
Finds all image references in a markdown file.

.DESCRIPTION
Parses a markdown file and extracts all image references (both inline and reference-style).
Parses a markdown file and extracts all image references including:
- Standard markdown format: ![alt text](image_path "optional title")
- Obsidian format: ![[image_path|alt text]]
Returns information about each image including the original markdown syntax, image path, alt text, and title.

.PARAMETER File
Expand All @@ -23,17 +25,59 @@ function Find-MarkdownImages {
$content = Get-Content -Path $File -Raw
$images = @()
$fileDirectory = Split-Path -Path $File -Parent

# If the file is in the current directory, use the current directory
if ([string]::IsNullOrEmpty($fileDirectory)) {
$fileDirectory = "."
}

# Regex pattern for inline images: ![alt text](image_path "optional title")
$inlinePattern = '!\[([^\]]*)\]\(([^)]+?)(?:\s+"([^"]*)")?\)'
# Regex pattern for standard markdown images: ![alt text](image_path "optional title")
$standardPattern = '!\[([^\]]*)\]\(([^)]+?)(?:\s+"([^"]*)")?\)'

# Find all inline image matches
$inlineMatches = [regex]::Matches($content, $inlinePattern)
# Regex pattern for Obsidian images: ![[image_path|alt text]]
$obsidianPattern = '!\[\[([^|\]]+?)(?:\|([^\]]*))?\]\]'

foreach ($match in $inlineMatches) {
$altText = $match.Groups[1].Value
$imagePath = $match.Groups[2].Value.Trim()
$title = if ($match.Groups[3].Success) { $match.Groups[3].Value } else { "" }
# Collect all matches with their positions
$allMatches = @()

# Find all standard markdown image matches
$standardMatches = [regex]::Matches($content, $standardPattern)
foreach ($match in $standardMatches) {
$allMatches += @{
Match = $match
Position = $match.Index
Format = "Standard"
}
}

# Find all Obsidian image matches
$obsidianMatches = [regex]::Matches($content, $obsidianPattern)
foreach ($match in $obsidianMatches) {
$allMatches += @{
Match = $match
Position = $match.Index
Format = "Obsidian"
}
}

# Sort matches by position in document
$allMatches = $allMatches | Sort-Object Position

# Process each match in document order
foreach ($matchInfo in $allMatches) {
$match = $matchInfo.Match
$format = $matchInfo.Format

if ($format -eq "Standard") {
$altText = $match.Groups[1].Value
$imagePath = $match.Groups[2].Value.Trim()
$title = if ($match.Groups[3].Success) { $match.Groups[3].Value } else { "" }
} else {
# Obsidian format
$imagePath = $match.Groups[1].Value.Trim()
$altText = if ($match.Groups[2].Success) { $match.Groups[2].Value } else { "" }
$title = "" # Obsidian format doesn't support titles
}

# Skip URLs (images already hosted online)
if ($imagePath -match '^https?://') {
Expand Down
9 changes: 5 additions & 4 deletions src/public/Update-MarkdownImages.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
Updates markdown content by replacing local image references with Google Drive URLs.

.DESCRIPTION
Takes a markdown file and replaces local image references with Google Drive public URLs,
while preserving alt text and titles.
Takes a markdown file and replaces local image references with Google Drive public URLs,
converting all images to standard markdown format while preserving alt text and titles.

.PARAMETER File
The path to the markdown file to update.
Expand All @@ -15,9 +15,10 @@ Each object should have OriginalMarkdown and NewUrl properties.

.EXAMPLE
$mappings = @(
@{ OriginalMarkdown = "![alt](local.jpg)"; NewUrl = "https://drive.google.com/uc?export=view&id=123" }
@{ OriginalMarkdown = "![alt](local.jpg)"; NewUrl = "https://drive.google.com/uc?export=view&id=123"; AltText = "alt"; Title = "" }
@{ OriginalMarkdown = "![[image.png|description]]"; NewUrl = "https://drive.google.com/uc?export=view&id=456"; AltText = "description"; Title = "" }
)
Update-MarkdownImageUrls -File "post.md" -ImageMappings $mappings
Update-MarkdownImages -File "post.md" -ImageMappings $mappings
#>
function Update-MarkdownImages {
[CmdletBinding()]
Expand Down
Loading