Polyglot Manifest License and End-of-Life Gate avatar

Polyglot Manifest License and End-of-Life Gate

Pricing

from $20.00 / 1,000 manifest set run starts

Go to Apify Store
Polyglot Manifest License and End-of-Life Gate

Polyglot Manifest License and End-of-Life Gate

Read manifest files from more than one ecosystem. Report the license, the deprecation flag and the abandonment age of each package in one table.

Pricing

from $20.00 / 1,000 manifest set run starts

Rating

0.0

(0)

Developer

kingii98

kingii98

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Categories

Share

Read manifest files from more than one ecosystem. Report the license, the deprecation flag and the abandonment age of each package in one table.

A team that runs Node, Python, Go and Rust must usually run one license tool for each ecosystem, and must then join the results by hand. This Actor reads all four manifest kinds in one run and writes one flat table, plus one run summary with a pass or fail gate verdict.

Who this is for

An engineering lead or a legal reviewer at a small polyglot team. This person must approve a release, or must answer a due-diligence question, and has no commercial software composition analysis platform.

Run it once for each release tag, once each month as a compliance sweep, or once before a due-diligence hand-over. The manifest set stays the same, so the run repeats with no new setup.

What the Actor does

  1. Reads 1 to 25 manifest files. Give each one as pasted text or as a public URL.
  2. Parses each manifest and finds the packages it names.
  3. Resolves each package against its public registry: npm, PyPI, crates.io or the Go module proxy with deps.dev.
  4. Writes one dataset row for each package in each manifest, and one run summary record.
  5. Stores a baseline document for the manifest set, so the next run can name what changed.

The Actor makes HTTP GET calls only. It does not clone a repository, install a package, or run any manifest content. It uses no browser, no proxy, no language model and no paid API.

Supported manifest types

Manifest typeEcosystemRegistry
package.jsonnpmregistry.npmjs.org
package-lock.jsonnpmregistry.npmjs.org
requirements.txtPyPIpypi.org
go.modGoproxy.golang.org and api.deps.dev
Cargo.tomlcrates.iocrates.io

The manifest type comes from the file name, so services/api/go.mod is a go.mod. You can also state the type field when the file name does not say.

Input

FieldTypeDefaultMeaning
manifestsarrayfour-ecosystem sample set1 to 25 manifests. Each item takes name, an optional type, and either content (pasted text) or url (a public URL).
licensePolicyarray of strings["GPL-3.0-only", "AGPL-3.0-only"]SPDX identifiers the report must flag.
abandonmentDaysinteger730A package with no new release in this many days gets the abandoned flag.
includeTransitivebooleanfalseResolve one level of transitive dependencies.
failOnDeprecatedbooleantrueA deprecated package counts as a policy failure.
failOnAbandonedbooleanfalseAn abandoned package counts as a policy failure.
failOnUnknownLicensebooleanfalseA license the Actor cannot read as SPDX counts as a policy failure.
baselineKeystring""Key-value store key for the baseline. Empty means the Actor derives one key from the manifest names.

Every field has a default, so a run with empty input {} succeeds and reports the sample manifest set.

A manifest given as a URL

{
"manifests": [
{ "name": "go.mod", "url": "https://raw.githubusercontent.com/owner/repo/main/go.mod" }
]
}

A URL must be public. The Actor refuses loopback, private and reserved addresses, refuses credentials in the URL, checks every redirect hop against the same rule, allows at most 3 redirects, and reads at most 2 MB for each manifest.

Output

Package rows

One row for each package in each manifest.

FieldMeaning
recordTypeAlways package.
manifestNameThe manifest the package came from.
ecosystemnpm, PyPI, Go or crates.io.
packageNameThe package name.
dependencyKindDIRECT or TRANSITIVE.
requestedRangeThe range the manifest states.
resolvedVersionThe version the Actor reports on.
latestVersionThe newest version the registry lists.
spdxLicenseThe SPDX expression, or UNKNOWN.
licenseIdentifiersThe SPDX identifiers in the expression.
licensePolicyVerdictPASS, FAIL or UNKNOWN.
deniedLicenseIdentifiersThe identifiers your policy denies.
deprecatedTrue when the registry marks the release deprecated.
deprecationMessageThe message the registry gives.
lastPublishDateThe newest release date of the package.
daysSinceLastPublishDays from that date to the run.
abandonedTrue when daysSinceLastPublish reaches abandonmentDays.
repositoryUrlThe source repository the registry states.
repositoryArchivedAlways null in version 1. See the limitations.
repositoryArchivedStatusAlways NOT_CHECKED in version 1.
resolutionStatusRESOLVED, NOT_FOUND or ERROR.
resolutionErrorThe reason the package did not resolve.
policyFailureTrue when the row breaks the policy.
policyFailureReasonsOne line for each reason.
baselineStatusBASELINE_CREATED, NEW, CHANGED or UNCHANGED.
changesSinceBaselineField, previous value and current value for each change.

Run summary

One record with recordType set to run-summary. It carries gateVerdict (PASS or FAIL), packageCountByEcosystem, policyFailureCount, deprecatedCount, abandonedCount, unresolvedPackageCount, resolutionRate, manifestErrors, the baseline counts (baselineFirstRun, changedPackageCount, newPackageCount, removedPackageCount), and truncated with packageLimit.

The gate verdict is FAIL when at least one row is a policy failure.

truncated is true when the manifest set holds more packages than packageLimit, and the table therefore shows only the first packageLimit packages. Read it before you treat the table as a complete report.

The baseline

The Actor keeps one baseline document for each manifest set in the named key-value store pkg-eol-gate-baselines. The baseline holds the resolved version, the license, the license verdict, the deprecation flag and the abandonment flag of each package. One baseline slot is one package inside one manifest (ecosystem|package|manifest), because two manifests can request different ranges of the same package and therefore hold different values.

  • The first run writes the baseline and reports the full table. Every row gets BASELINE_CREATED and reports no change.
  • A later run compares each row against the baseline and marks it NEW, CHANGED or UNCHANGED. A changed row names each field that moved.

The default store cannot hold the baseline, because Apify purges it when a run starts.

The run always succeeds

A failed gate, an unreachable manifest URL, a package the registry does not know, and an empty manifest are all report results, not malfunctions. The run ends with status SUCCEEDED and carries the verdict in the status message and in the run-summary record. Read gateVerdict to gate a release.

Invalid input is reported the same way: the Actor writes one record with gateVerdict set to ERROR and an inputError message, and the run succeeds.

Pricing: pay per event

EventUnitCounted as
manifest-set-run-startone runCharged once, at the start of the run.
package-resolvedone unique package resolved against a registryOne charge for each unique (ecosystem, package name) pair sent to a registry. A package that two manifests name is charged once, also when the two manifests request different ranges of it. A lookup that ends NOT_FOUND or ERROR was still sent to a registry, so it is charged.
policy-failure-recordedone policy failure written to the reportOne charge for each failing row in the dataset. A failing package that two manifests name writes two rows, so it is charged twice.

Bounds

BoundValue
Manifests for each run25
Characters for each pasted manifest2 000 000
Bytes for each fetched manifest2 000 000
Packages for each run1 500
Redirects for each manifest URL3
Registry requests in parallel6
Timeout for each request25 s
Bytes for each registry response8 000 000
Denied SPDX identifiers100

Version 1 limitations

  • Version resolution is a documented rule, not a solver. Each manifest is resolved against the range that it states, so two manifests that request different majors of one package get their own row, version and license. An exact version in the manifest wins. A caret or tilde range takes the highest registry release inside the range. Anything else takes the highest stable release. This is not what a real package manager resolves in every case. For an exact answer, give a lock file.
  • The repository archived flag is not read. Version 1 makes no call to a source forge. The GitHub API does give archived without a token, but only at 60 requests each hour for each address, which a shared Actor cannot hold to. The column is present and holds null, with repositoryArchivedStatus set to NOT_CHECKED.
  • PyPI and crates.io have no deprecation flag. A yanked release is the nearest equivalent, and it carries the same instruction: do not use this release. The Actor reports a yanked release as deprecated and says so in deprecationMessage.
  • Transitive resolution covers npm and PyPI only. Those two registries state a package's dependencies in the same document the Actor already reads. crates.io does not, so a Cargo transitive level would need one more call for each crate. A Go go.mod already lists its indirect requirements, and the Actor keeps them when includeTransitive is on.
  • A license the registry does not state as SPDX is reported as UNKNOWN, not guessed. Turn on failOnUnknownLicense when an unread license must stop a release.
  • The abandonment age measures the package, not the pinned release. It uses the newest release date of the whole package, which is the right measure for "is this project still maintained".

Local development

uv sync
uv run pytest
uv run ruff check .