Dependency Release Cooldown Admission Ledger avatar

Dependency Release Cooldown Admission Ledger

Pricing

from $12.00 / 1,000 run_starteds

Go to Apify Store
Dependency Release Cooldown Admission Ledger

Dependency Release Cooldown Admission Ledger

Check each pinned dependency version against a release-age cooldown policy. Report which pins are still inside the window, and the date on which each one becomes admissible.

Pricing

from $12.00 / 1,000 run_starteds

Rating

0.0

(0)

Developer

kingii98

kingii98

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Categories

Share

Dependency Release Cooldown Admission Ledger for Mixed Ecosystems

Check each pinned dependency version against a release-age cooldown policy. The Actor reports which pins are still inside the cooldown window, and the date on which each one becomes admissible.

A cooldown policy says: do not adopt a release until it is at least N days old. uv applies such a policy with its exclude-newer setting, and other tools block fresh releases by default. Other ecosystems have no such setting: the Maven versions plugin, for example, has no release-age filter. This Actor applies one policy across npm, PyPI, Maven Central and NuGet, and gives the part that the native tools leave out: the date on which each blocked pin becomes admissible. A blocked list is a stop sign; a dated calendar is a plan.

What it does

  1. Reads a bounded list of pins in the form ecosystem:name@version.
  2. Asks each package registry for the release record of the package.
  3. Compares the publish timestamp of the pinned version with the as-of date.
  4. Writes one dataset row for each pin, one run summary, and one admission calendar when at least one pin is blocked.

The verdict is a function of three values only: the publish timestamp, the as-of date and the cooldown in days. The Actor holds no state between runs, so a run made today with an as-of date of last month gives the same answer as the run made last month. That is what an audit needs.

Input

FieldTypeDefaultMeaning
pinsarray of textfive example pins1 to 1000 pins, each one ecosystem:name@version. One entry may hold several lines.
cooldownDaysinteger30A release younger than this many days at the as-of date is inside the window. 0 to 3650.
asOftextrun dateThe date the policy is applied, YYYY-MM-DD. The field has no schema default: an omitted or empty asOf is always the run date, which is what a scheduled run needs.
exemptPackagesarray of text["lodash"]Names the policy does not cover, for example first-party packages.

Every field has a default, so a run with an empty input {} succeeds.

Pin format

EcosystemPrefixExample
npmnpmnpm:express@4.18.2, npm:@babel/core@7.24.0
PyPIpypipypi:requests@2.31.0
Maven Centralmavenmaven:com.google.guava:guava@32.1.3-jre
NuGetnugetnuget:Newtonsoft.Json@13.0.3

A Maven pin names the group and the artifact, separated by a colon. The aliases node, pip, python, maven-central, java and dotnet are accepted as well.

An exemption matches without regard to case, in the registry spelling or in the normalized spelling (Flask_SQLAlchemy matches flask-sqlalchemy). For a Maven pin the artifact alone also matches. Write ecosystem:name, for example npm:express, to exempt the package in one ecosystem only.

Output

One dataset row for each pin (recordType pin):

FieldMeaning
pinThe pin as the buyer wrote it.
ecosystem, packageName, versionThe three parts of the pin.
publishedAtThe publish timestamp from the registry release record.
ageDaysThe age of the release at the as-of date. It is negative when the release is newer than the as-of date.
cooldownDaysThe policy this run applied.
verdictSee the table below.
admissibleOnThe date the pin leaves the window. For an admissible pin it is the date it left the window, which an audit can read.
daysRemainingDays from the as-of date to admissibleOn, 0 for an admissible pin.
newestAdmissibleVersionThe highest release that already satisfies the policy at the as-of date.
newestAdmissiblePublishedAtThe publish timestamp of that release.
asOf, noteThe date applied, and the reason when a verdict needs one.
VerdictMeaning
admissibleThe release is at least cooldownDays old at the as-of date.
inside_cooldownThe release is younger than the policy allows. admissibleOn says when that ends.
unknown_publish_dateThe registry has no publish date for this version: the package or the version is not found, or the registry call failed. note says which.
exemptThe package is on the exempt list, so the policy is not applied and no registry call is made.
invalid_pinThe entry could not be read. note says why. The run continues.

One run-summary row (recordType runSummary) holds countsByVerdict, the count per ecosystem, earliestAdmissionDate (the first date on which any blocked pin becomes admissible) and fullSetAdmissibleOn (the date on which the whole set becomes admissible).

One admission-calendar row (recordType admissionCalendar) groups the blocked pins by the date on which each becomes admissible. It is written only when the run finds at least one blocked pin, and it is also stored in the key-value store under the key admission-calendar.

A blocked pin, an unreachable registry and an empty pin list are business results. They are reported in the dataset and in the run status message, and the run succeeds. A failed run means a real malfunction.

Pricing (pay per event)

EventUnitCounted as
run_startedone runCharged once at the start of every run, before the input is read, so that a run stopped early still pays for the work it started.
pin_checkedone pin checkedOne unit for each pin checked against the policy. An exempt pin and an unreadable pin are not checked, so they are not charged. A pin with an unknown publish date is charged, because the registry lookup was made.
admission_calendar_builtone run with blocked pinsCharged once, and only for a run that finds at least one blocked pin and therefore builds the calendar.

Two pins of one package cost one registry call, which keeps the run cost below the charge.

Bounds and safety

  • At most 1000 pins and 500 exempt names in one run.
  • Only four fixed hosts are contacted: registry.npmjs.org, pypi.org, search.maven.org and api.nuget.org. No host, scheme or path comes from the input, so the Actor sends no request to a private or reserved address. The one URL a registry itself supplies, a NuGet registration page, is used only when it stays on api.nuget.org.
  • 25 s timeout for each call, at most 6 calls in parallel, at most 12 MB for each response body.
  • HTTP only. No browser, no proxy, no login and no secret.
  • A registry failure for one package is reported on that package's rows. It does not end the run.

Version 1 limitations

  • Version ordering uses one rule for the four ecosystems: the numeric parts are compared as numbers, a stable release sorts above its own prereleases, and the remaining text is compared as text. A Maven classifier-style suffix such as -jre is not read as a prerelease marker, but two such suffixes on one version number are ordered as text.
  • newestAdmissibleVersion leaves out prereleases, unless the pin itself is a prerelease, and always leaves out a yanked or unlisted release.
  • Maven Central is read through the search API. The run asks for each pinned version exactly, then reads the 200 newest releases of the coordinate for the newest-admissible answer.
  • A NuGet package with many registration pages is read to a bound: the pages that hold a pinned version first, then the newest pages, at most 6 fetched pages.
  • The publish timestamp is the one the registry states. A registry that restates a release changes the answer.

Repeat use

The result changes without any action by the buyer, because a pin that is blocked today is admissible tomorrow. Run the Actor on a daily or weekly schedule and read fullSetAdmissibleOn to plan the merge window.

Development

uv sync
uv run pytest
uv run ruff check .

To run the Actor against the default fixture:

mkdir -p storage/key_value_stores/default
cp .actor/default_input.json storage/key_value_stores/default/INPUT.json
APIFY_LOCAL_STORAGE_DIR=$PWD/storage uv run python -m cooldown_ledger

The default input states a fixed as-of date, so the example run gives the same answer on every day: three admissible pins, one pin inside the window with an admission date, and one exempt pin. Only this fixture holds that date. A run that does not state asOf uses the run date.