puppetlabs/puppet · error · Puppet::Error

Mac OS X PKG DMGs must specify a source string ending in .dm

Error message

Mac OS X PKG DMGs must specify a source string ending in .dmg or flat .pkg file

What it means

Raised by `installpkgdmg` (lib/puppet/provider/package/pkgdmg.rb:80), Puppet's macOS pkgdmg provider. The `source` attribute must end in `.dmg` or `.pkg` (case-insensitive) because the suffix selects the strategy: mount a disk image with hdiutil, or install a flat package with `installer -pkg`. Any other extension fails fast before anything is downloaded.

Source

Thrown at lib/puppet/provider/package/pkgdmg.rb:80

  end

  def self.installpkg(source, name, orig_source)
    installer "-pkg", source, "-target", "/"
    # Non-zero exit status will throw an exception.
    Puppet::FileSystem.open("/var/db/.puppet_pkgdmg_installed_#{name}", nil, "w:UTF-8") do |t|
      t.print "name: '#{name}'\n"
      t.print "source: '#{orig_source}'\n"
    end
  end

  def self.installpkgdmg(source, name)
    unless Puppet::Util::HttpProxy.no_proxy?(source)
      http_proxy_host = Puppet::Util::HttpProxy.http_proxy_host
      http_proxy_port = Puppet::Util::HttpProxy.http_proxy_port
    end

    unless source =~ /\.dmg$/i || source =~ /\.pkg$/i
      raise Puppet::Error, _("Mac OS X PKG DMGs must specify a source string ending in .dmg or flat .pkg file")
    end

    require 'open-uri' # Dead code; this is never used. The File.open call 20-ish lines south of here used to be Kernel.open but changed in '09. -NF
    cached_source = source
    tmpdir = Dir.mktmpdir
    ext = /(\.dmg|\.pkg)$/i.match(source)[0]
    begin
      if %r{\A[A-Za-z][A-Za-z0-9+\-.]*://} =~ cached_source
        cached_source = File.join(tmpdir, "#{name}#{ext}")
        args = ["-o", cached_source, "-C", "-", "-L", "-s", "--fail", "--url", source]
        if http_proxy_host and http_proxy_port
          args << "--proxy" << "#{http_proxy_host}:#{http_proxy_port}"
        elsif http_proxy_host and !http_proxy_port
          args << "--proxy" << http_proxy_host
        end
        begin
          curl(*args)
          Puppet.debug "Success: curl transferred [#{name}] (via: curl #{args.join(' ')})"

View on GitHub (pinned to e227c27540)

Solutions

  1. Mirror the artifact as a .dmg or a flat .pkg and point `source` at a URL that literally ends with that extension.
  2. Strip query strings/fragments from the URL so the regex sees the extension.
  3. For .zip or .mpkg payloads, repackage them (a flat .pkg inside a dmg) or use a different provider.
  4. Verify case: the check is case-insensitive, so only the extension itself matters.

Example fix

# before
package { 'firefox':
  ensure   => installed,
  provider => pkgdmg,
  source   => 'https://download.example.com/firefox-latest.tar.bz2',
}

# after
package { 'firefox':
  ensure   => installed,
  provider => pkgdmg,
  source   => 'https://download.example.com/mirror/Firefox-115.0.dmg',
}
Defensive patterns

Strategy: validation

Validate before calling

# Validate the source extension before declaring the resource
source='https://mirror.example.com/Firefox-115.0.dmg'
[[ "$source" =~ \.(dmg|pkg)$ ]] || { echo "source must end in .dmg or .pkg"; exit 1; }

Prevention

When it happens

Trigger: `package { 'Firefox': provider => pkgdmg, source => 'https://.../Firefox.tar.bz2' }`, a CDN/redirect URL whose final path lacks the extension, or a URL with a query string after the filename.

Common situations: Pointing source at a .zip archive or an .mpkg bundle (not supported - only flat .pkg); vendor download links that redirect to extension-less URLs; typos in the source.

Related errors


AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21). Data as JSON: /api/errors/cd44dcda71e053b3. Report an issue: GitHub.