Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

action-release-checksums

GitHub Action that generates a per-file checksum manifest for every WordPress plugin or theme zip in a directory. Each <name>.zip gets a <name>.json next to it, listing the MD5 and SHA-256 of every file inside the archive — the same format WordPress.org publishes for plugins at downloads.wordpress.org/plugin-checksums/<slug>/<version>.json.

Publish the manifest alongside your release and anything that can download it (a support tool, an integrity checker, WP-CLI-style verification) can tell whether the files on a site still match what you shipped.

The action only generates files. Uploading the zip and its manifest is up to your workflow, with whatever tool you already use.

Usage

- name: Build release zip
  run: ./build.sh            # produces artifact/my-plugin.zip

- name: Generate checksum manifests
  uses: Codeinwp/action-release-checksums@main
  with:
    artifact_dir: artifact   # default

- name: Upload release
  run: aws s3 sync artifact/ s3://my-bucket/releases/my-plugin/${{ github.ref_name }}/
  # artifact/ now contains my-plugin.zip and my-plugin.json

Works with any number of zips in the directory — one manifest is written per zip.

Inputs

Input Default Description
artifact_dir artifact Directory containing the release *.zip files
out_dir same as artifact_dir Where to write the <name>.json manifests
strict true Fail the job if any zip cannot be processed (corrupt archive, empty archive, or no plugin/theme header found). Set to false to skip such zips with a warning instead.

Outputs

Output Description
manifests JSON array of the manifest file paths that were written

Manifest format

{
  "plugin": "my-plugin",
  "version": "1.4.2",
  "type": "plugin",
  "source": "https://github.com/org/repo/actions/runs/123456789",
  "zip": "my-plugin.zip",
  "generated_at": "2026-01-01T12:00:00.000Z",
  "files": {
    "my-plugin.php":        { "md5": "", "sha256": "" },
    "includes/class-a.php": { "md5": "", "sha256": "" }
  }
}
  • The top-level key is plugin or theme, holding the slug.
  • Slug = the zip's root folder (the conventional my-plugin/... layout, which is also the directory the product is installed into). Flat zips fall back to the zip's base name.
  • Type and version come from the file headers inside the archive: Theme Name: / Version: in style.css for themes, Plugin Name: / Version: in the main PHP file for plugins.
  • files keys are paths relative to the root folder, /-separated, sorted. Directories and symbolic links are never listed.
  • source is the URL of the workflow run that produced the manifest (empty when run outside GitHub Actions).

Because files uses the same shape as WordPress.org's plugin checksums, a consumer can treat both sources identically.

Verifying an install

Any language works; the check is just "hash the local file, compare". For example, in PHP:

$manifest = json_decode( file_get_contents( $manifest_url ), true );
foreach ( $manifest['files'] as $path => $hashes ) {
    $local = $install_dir . '/' . $path;
    if ( ! is_file( $local ) ) {
        $missing[] = $path;
    } elseif ( hash_file( 'sha256', $local ) !== $hashes['sha256'] ) {
        $modified[] = $path;
    }
}

Requirements

  • A runner with unzip and Node.js 20+ (both present on the hosted ubuntu-latest image). No npm dependencies are installed at run time.

Running locally

node src/generate.js --artifact-dir path/to/zips [--out-dir path] [--no-strict]

Development

npm test   # node --test, no dependencies

License

GPL-2.0-or-later

About

Generate per-file checksum manifests next to release zips (served by api.themeisle.com/checksum)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages