Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Asset validation failed. Invalid product archive. #327

Closed
sanjubhambhani opened this issue Jul 17, 2024 · 8 comments · Fixed by #340
Closed

Asset validation failed. Invalid product archive. #327

sanjubhambhani opened this issue Jul 17, 2024 · 8 comments · Fixed by #340
Assignees
Labels

Comments

@sanjubhambhani
Copy link

sanjubhambhani commented Jul 17, 2024

Node: v20.15.0
Electron: v31.1.0
Electron Forge: v7.4.0

Working on submitting a "mas" build to the Mac App Store. The build is packaged, code-signed and sandboxed correctly. However uploading the pkg file on Transporter for verification returns the following errors:

Asset validation failed (90230)
Invalid product archive metadata. Error in keyPath [product-metadata.product-identifier]. Please re-build your archive with an up-to-date version of Xcode, and submit again. (ID: ed4e62c9-8b3a-41f0-ad67-c54c4c479459)

Asset validation failed (90264)
Invalid product archive. The lowest minimum system version in the product definition property list, none, must equal the LSMinimumSystemVersion value, 10.15. (ID: 123ed95f-a48c-44c7-84e4-1a56c7a16203)

Asset validation failed (90230)
Invalid product archive metadata. Error in keyPath [product-metadata.product-version]. Please re-build your archive with an up-to-date version of Xcode, and submit again. (ID: 363b8f8f-a457-4511-9e04-f1d981719f63)

I have the latest version of Xcode and confirmed the app name, version, build number and build identifier are all correct. They are all appearing in the Info.plist file in the build app as expected.

Anyone having the same issue and/or have a fix?!

@Jeepeng
Copy link

Jeepeng commented Jul 17, 2024

I encountered the same issue, and downgrading to version 1.3.0 worked for me. #328

@sanjubhambhani
Copy link
Author

I encountered the same issue, and downgrading to version 1.3.0 worked for me. #328

appreciate the heads up, was stuck in this for a whole day!

@mahnunchik
Copy link

mahnunchik commented Dec 10, 2024

I've faced the same issue. Downgrading to version 1.3.0 from 1.3.1 solves the issue

Asset validation failed (90264)
Invalid product archive. The lowest minimum system version in the product definition property list, none, must equal the LSMinimumSystemVersion value, 11.0. (ID: 8f4095d1-e2a1-4594-8750-00296535b03d)

Asset validation failed (90230)
Invalid product archive metadata. Error in keyPath [product-metadata.product-identifier]. Please re-build your archive with an up-to-date version of Xcode, and submit again. (ID: 6cb7bcbb-268b-4c82-8f84-87a6e7f84f69)

@mahnunchik
Copy link

It seems there is regression 51531f1

@rickymohk @erickzhao

@erickzhao
Copy link
Member

Thanks for the heads-up @mahnunchik. I'll assign this issue to myself and see what the best solution is here.

@erickzhao erickzhao self-assigned this Dec 10, 2024
@erickzhao
Copy link
Member

The regression seems related to this (from the manpage of productbuild):

When creating product archives for submission to the Mac App Store, use only the --component mode of productbuild. The other modes will create product archives that are compatible with the OS X Installer, but are not necessarily acceptable for the Mac App Store.

https://www.unix.com/man-page/osx/1/productbuild/

@rickymohk
Copy link
Contributor

rickymohk commented Dec 10, 2024

I raised this on stackoverflow (https://stackoverflow.com/questions/78265335/invalid-product-archive-metadata-when-uploading-electron-app-to-mac-app-store-us/78266218#78266218) and figured out the solution myself. I end up making the following changes in my local copy to get it work on Mac App Store.

type.ts - adding product options, which points to a product_definition.plist

type OnlyFlatOptions = {
  identityValidation?: boolean;
  install?: string;
  pkg?: string;
  scripts?: string;
  product?:string;
};

The product_definition.plist looks like this

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>os</key>
  <array>
    <string>10.11.0</string>
  </array>
</dict>
</plist>

flat.ts - adding --identifier, --version and --product args to productbuild. Added import "plist" and "fs-extra" packages

async function buildApplicationPkg (opts: ValidatedFlatOptions, identity: Identity) {
  const componentPkgPath = path.join(path.dirname(opts.app), path.basename(opts.app, '.app') + '.pkg');
  const pkgbuildArgs = ['--install-location', opts.install, '--component', opts.app, componentPkgPath];
  if (opts.scripts) {
    pkgbuildArgs.unshift('--scripts', opts.scripts);
  }
  await execFileAsync('pkgbuild', pkgbuildArgs);

  const appInfoPath = path.join(getAppContentsPath(opts), 'Info.plist');
  const appInfoContents = await fs.readFile(appInfoPath, 'utf8');
  const appInfo = plist.parse(appInfoContents) as Record<string, any>;
  const version = appInfo["CFBundleShortVersionString"] as string;
  const identifier = appInfo["CFBundleIdentifier"] as string;

  const args = [
    '--package', componentPkgPath, 
    opts.install, 
    ...(identifier ? ['--identifier', identifier,] : []),
    ...(version ? ['--version', version,] : []),
    '--sign', identity.name,
    ...(opts.product ? ['--product', opts.product,] : []),
    opts.pkg
  ];
  if (opts.keychain) {
    args.unshift('--keychain', opts.keychain);
  }

  debugLog('Flattening... ' + opts.app);
  await execFileAsync('productbuild', args);
  await execFileAsync('rm', [componentPkgPath]);
}

There were also some changes regarding preAutoEntitlements.

sign.ts - only apply preAutoEntitlements to top level app bundle

async function signApplication (opts: ValidatedSignOptions, identity: Identity) {
  function shouldIgnoreFilePath (filePath: string) {
    if (opts.ignore) {
      return opts.ignore.some(function (ignore) {
        if (typeof ignore === 'function') {
          return ignore(filePath);
        }
        return filePath.match(ignore);
      });
    }
    return false;
  }

  const children = await walkAsync(getAppContentsPath(opts));

  if (opts.binaries) children.push(...opts.binaries);

  const args = ['--sign', identity.hash || identity.name, '--force'];
  if (opts.keychain) {
    args.push('--keychain', opts.keychain);
  }

  /**
   * Sort the child paths by how deep they are in the file tree.  Some arcane apple
   * logic expects the deeper files to be signed first otherwise strange errors get
   * thrown our way
   */
  children.sort((a, b) => {
    const aDepth = a.split(path.sep).length;
    const bDepth = b.split(path.sep).length;
    return bDepth - aDepth;
  });

  for (const filePath of [...children, opts.app]) {
    if (shouldIgnoreFilePath(filePath)) {
      debugLog('Skipped... ' + filePath);
      continue;
    }

    const perFileOptions = await mergeOptionsForFile(
      opts.optionsForFile ? opts.optionsForFile(filePath) : null,
      defaultOptionsForFile(filePath, opts.platform)
    );

    // preAutoEntitlements should only be applied to the top level app bundle.
    // Applying it other files will cause the app to crash and be rejected by Apple.
    if (!filePath.includes('.app/')) {
      if (opts.preAutoEntitlements === false) {
        debugWarn('Pre-sign operation disabled for entitlements automation.');
      } else {
        debugLog(
          'Pre-sign operation enabled for entitlements automation with versions >= `1.1.1`:',
          '\n',
          '* Disable by setting `pre-auto-entitlements` to `false`.'
        );
        if (!opts.version || compareVersion(opts.version, '1.1.1') >= 0) {
          // Enable Mac App Store sandboxing without using temporary-exception, introduced in Electron v1.1.1. Relates to electron#5601
          const newEntitlements = await preAutoEntitlements(opts, perFileOptions, {
            identity,
            provisioningProfile: opts.provisioningProfile
              ? await getProvisioningProfile(opts.provisioningProfile, opts.keychain)
              : undefined
          });

          // preAutoEntitlements may provide us new entitlements, if so we update our options
          // and ensure that entitlements-loginhelper has a correct default value
          if (newEntitlements) {
            perFileOptions.entitlements = newEntitlements;
          }
        }
      }
    }

    debugLog('Signing... ' + filePath);

    const perFileArgs = [...args];

    if (perFileOptions.requirements) {
      if (perFileOptions.requirements.charAt(0) === '=') {
        perFileArgs.push(`-r${perFileOptions.requirements}`);
      } else {
        perFileArgs.push('--requirements', perFileOptions.requirements);
      }
    }
    if (perFileOptions.timestamp) {
      perFileArgs.push('--timestamp=' + perFileOptions.timestamp);
    } else {
      perFileArgs.push('--timestamp');
    }

    let optionsArguments: string[] = [];

    if (perFileOptions.signatureFlags) {
      if (Array.isArray(perFileOptions.signatureFlags)) {
        optionsArguments.push(...perFileOptions.signatureFlags);
      } else {
        const flags = perFileOptions.signatureFlags.split(',').map(function (flag) {
          return flag.trim();
        });
        optionsArguments.push(...flags);
      }
    }

    if (perFileOptions.hardenedRuntime || optionsArguments.includes('runtime')) {
      // Hardened runtime since darwin 17.7.0 --> macOS 10.13.6
      if (compareVersion(osRelease, '17.7.0') >= 0) {
        optionsArguments.push('runtime');
      } else {
        // Remove runtime if passed in with --signature-flags
        debugLog(
          'Not enabling hardened runtime, current macOS version too low, requires 10.13.6 and higher'
        );
        optionsArguments = optionsArguments.filter((arg) => {
          return arg !== 'runtime';
        });
      }
    }

    if (optionsArguments.length) {
      perFileArgs.push('--options', [...new Set(optionsArguments)].join(','));
    }

    await execFileAsync(
      'codesign',
      perFileArgs.concat('--entitlements', perFileOptions.entitlements, filePath)
    );
  }

  // Verify code sign
  debugLog('Verifying...');
  await verifySignApplication(opts);
  debugLog('Verified.');

  // Check entitlements if applicable
  debugLog('Displaying entitlements...');
  const result = await execFileAsync('codesign', [
    '--display',
    '--entitlements',
    ':-', // Write to standard output and strip off the blob header
    opts.app
  ]);

  debugLog('Entitlements:', '\n', result);
}

My local copy is based on v1.1.0. I am not sure if any other things are difference since then. I also forgot how did I get the idea to make these changes earlier this year.

Copy link

🎉 This issue has been resolved in version 1.3.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants