Skip to content

dweekly/Sparkle-Validator

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

44 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Sparkle Validator logo

Sparkle Validator

A comprehensive validator for Sparkle appcast.xml feeds.
Available as a CLI tool, JavaScript library, and web application.

CI codecov npm version License: MIT

Note: This is an independent community project. It is not affiliated with, endorsed by, or sponsored by the official Sparkle project or its maintainers.

Methodology

This validator was developed by analyzing 500+ real-world appcasts from production macOS applications including iTerm2, VLC, Cyberduck, Brave Browser, Dash, Transmission, and many others. Our validation rules are grounded in:

  1. Sparkle Source Code — Direct analysis of version comparison logic, signature verification, and parsing behavior in the Sparkle 2.x codebase
  2. Maintainer Feedback — Rule refinements based on feedback from Sparkle maintainer @zorgiepoo
  3. Real-World Patterns — Identifying common issues that cause silent failures (missing versions, malformed signatures, date/version ordering mismatches)

The test corpus includes apps that are:

  • Perfect (zero warnings): LowProfile, Scroll Reverser, SourceTree, Skim
  • Real-world valid (minor warnings): iTerm2, Dash, Cyberduck, Tunnelblick
  • Edge cases: Large feeds (200+ items), delta-heavy feeds, multi-channel feeds

This empirical approach ensures the validator catches issues that actually matter in production while minimizing false positives.

Features

  • Validates Sparkle appcast.xml feeds against all known requirements
  • Reports errors, warnings, and informational messages with line numbers
  • Provides fix suggestions for common issues
  • Works as CLI, library, web app, or GitHub Action
  • Checks:
    • XML structure (RSS 2.0 + Sparkle namespace)
    • Version declarations
    • Enclosure attributes (url, length, type)
    • URL validity
    • Date formats (RFC 2822)
    • Signatures (EdDSA/DSA)
    • System requirements
    • Delta updates
    • Phased rollouts
    • Channel names
    • And more...

Web App

Try it online at SparkleValidator.com

CLI Installation

npm install -g sparkle-validator

Or run directly without installing:

npx sparkle-validator https://example.com/appcast.xml

Or with Homebrew:

brew tap dweekly/sparkle-validator
brew install sparkle-validator

CLI Usage

# Validate a local file
sparkle-validator appcast.xml

# Validate from URL
sparkle-validator https://example.com/appcast.xml

# Validate from stdin
cat appcast.xml | sparkle-validator -

# JSON output
sparkle-validator --format json appcast.xml

# Strict mode (warnings as errors)
sparkle-validator --strict appcast.xml

# Only show errors
sparkle-validator --quiet appcast.xml

# Check that URLs exist and sizes match
sparkle-validator --check-urls appcast.xml

# Check URLs with custom timeout (ms)
sparkle-validator --check-urls --timeout 30000 appcast.xml

CLI Options

Option Description
-f, --format <type> Output format: text (default) or json
-s, --strict Treat warnings as errors
-c, --check-urls Check that URLs exist and sizes match
--timeout <ms> Timeout for URL checks (default: 10000ms)
--no-info Suppress informational messages
--no-color Disable colored output
-q, --quiet Only show errors
-v, --version Show version number
-h, --help Show help

Exit Codes

Code Meaning
0 Valid (no errors)
1 Invalid (has errors, or warnings with --strict)
2 Input error (file not found, network error, etc.)

CI/CD Integration

GitHub Action

Use the official GitHub Action for the simplest integration:

name: Validate Appcast

on:
  push:
    paths: ['appcast.xml']
  pull_request:
    paths: ['appcast.xml']

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Validate appcast.xml
        uses: dweekly/Sparkle-Validator@v1
        with:
          file: appcast.xml

      # With options
      - name: Validate with URL checking
        uses: dweekly/Sparkle-Validator@v1
        with:
          file: appcast.xml
          strict: true
          check-urls: true

Action Inputs

Input Description Default
file Path or URL to appcast.xml (required)
strict Treat warnings as errors false
check-urls Verify enclosure URLs exist false
timeout URL check timeout (ms) 10000
quiet Only show errors false
format Output format: text or json text

Action Outputs

Output Description
valid true if no errors
error-count Number of errors
warning-count Number of warnings
info-count Number of info messages
json Full result as JSON

npx Alternative

      - name: Validate appcast.xml
        run: npx sparkle-validator appcast.xml

      # Strict mode (warnings fail the build)
      - name: Validate appcast.xml (strict)
        run: npx sparkle-validator --strict appcast.xml

      # JSON output for further processing
      - name: Validate and capture results
        run: |
          npx sparkle-validator --format json appcast.xml > validation.json
          cat validation.json

Validate Remote Appcast

      - name: Validate published appcast
        uses: dweekly/Sparkle-Validator@v1
        with:
          file: https://example.com/appcast.xml

Pre-commit Hook

# .git/hooks/pre-commit
#!/bin/sh
npx sparkle-validator appcast.xml || exit 1

Library Usage

import { validate } from 'sparkle-validator';

const xml = `<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
  <channel>
    <title>My App</title>
    <link>https://example.com</link>
    <item>
      <title>Version 2.0</title>
      <pubDate>Thu, 13 Jul 2023 14:30:00 -0700</pubDate>
      <sparkle:version>200</sparkle:version>
      <description><![CDATA[<p>New features!</p>]]></description>
      <enclosure url="https://example.com/app.zip"
                 length="12345678"
                 type="application/octet-stream"
                 sparkle:edSignature="eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA==" />
    </item>
  </channel>
</rss>`;

const result = validate(xml);

console.log(result.valid);       // true
console.log(result.errorCount);  // 0
console.log(result.diagnostics); // Array of diagnostics

ValidationResult

interface ValidationResult {
  valid: boolean;           // true if no errors
  diagnostics: Diagnostic[];
  errorCount: number;
  warningCount: number;
  infoCount: number;
}

interface Diagnostic {
  id: string;          // e.g. "E008", "W003"
  severity: "error" | "warning" | "info";
  message: string;
  line?: number;       // 1-based
  column?: number;     // 1-based
  path?: string;       // e.g. "rss > channel > item[2] > enclosure"
  fix?: string;        // Suggestion for fixing the issue
}

Validation Rules

Errors (E001-E031, excluding E026)

ID Description
E001 Not well-formed XML
E002 Root element is not <rss>
E003 Missing version="2.0" on <rss>
E004 Missing Sparkle namespace declaration
E005 Missing <channel> inside <rss>
E006 More than one <channel> element
E007 No <item> elements in <channel>
E008 Item missing sparkle:version
E009 Item has neither <enclosure> with url nor <link>
E010-E013 Enclosure missing/invalid attributes
E014-E018 Invalid URLs
E019 Invalid channel name characters
E020-E021 Phased rollout errors
E022 Invalid installationType
E023-E025 Delta update structure errors
E027 URL returns non-2xx status (--check-urls)
E028 Content-Length doesn't match declared length (--check-urls)
E029 Version string is empty or whitespace-only
E030 Invalid sparkle:os value (must be "macos" or "windows")
E031 Invalid Ed25519/DSA signature (malformed base64 or wrong length)

Warnings (W001-W043)

ID Description
W001-W002 Missing title on channel/item
W003-W004 Missing or invalid pubDate
W006 DSA-only signature (deprecated, use EdDSA)
W007-W008 Redundant version declarations
W009 No release notes
W010 Non-standard MIME type
W011-W013 System version format issues
W014 (Moved to I011)
W016 Unencoded URL characters
W017 informationalUpdate with enclosure
W018 Items not sorted by version (newest first)
W019 Enclosure length is 0
W020 Duplicate version
W021 URL redirects to different location (--check-urls)
W022 Content-Length header missing (--check-urls)
W023 Local/private URL skipped (--check-urls)
W024 URL uses insecure HTTP instead of HTTPS (--check-urls)
W025 pubDate is in the future
W026 pubDate is implausibly old (before 2001/Mac OS X era)
W027 Version string is non-numeric (may cause comparison failures)
W028 Version decreases while pubDate increases
W030 URL file extension doesn't match expected type
W031 (Moved to I012)
W032 Multiple delta enclosures for same deltaFrom
W033 shortVersionString format unusual (not x.y.z)
W034 criticalUpdate version attribute not valid format
W035 Feed mixes HTTP and HTTPS URLs
W036 hardwareRequirements contains unknown architecture
W037 releaseNotesLink missing xml:lang for localization
W038 CDATA section used in version/signature elements
W039 XML declaration missing encoding attribute
W040 Channel has language but items have different lang
W041 Version missing but deducible from filename (Sparkle fallback)
W042 Version only as enclosure attribute (prefer <sparkle:version> element)
W043 sparkle:os deprecated (prefer separate feeds per platform)

Info (I001-I012)

ID Description
I001 Summary: N items across M channels
I002 Item contains N delta updates
I003 Item uses phased rollout
I004 Item marked as critical update
I005 Item targets non-macOS platform
I006 Item requires specific hardware (Sparkle 2.9+)
I007 Item requires minimum app version to update (Sparkle 2.9+)
I008 Feed contains >50 items (performance consideration)
I009 Summary of OS support range across all items
I010 Enclosure has no signature (signatures are optional)
I011 Missing channel link (informational)
I012 Delta references version not in feed (old versions may be pruned)

Development

# Install dependencies
npm install

# Run tests
npm test

# Build
npm run build

# Type check
npm run lint

Supply Chain Security

This package is published with:

  • npm provenance — cryptographically attests that the package was built from this repository via GitHub Actions
  • SBOM — Software Bill of Materials (CycloneDX format) attached to each GitHub release

You can verify provenance on npm: npm audit signatures

License

MIT License - see LICENSE for details.

Contributing

See CONTRIBUTING.md for guidelines.

About

Validate Sparkle appcast.xml feeds — CLI tool, library, and web app

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors