# GitHub Actions Reference Pin and Archived-Action Inventory (`kingii98/github-actions-reference-pin-and-archived-action-inventory`) Actor

Reads the workflow files of a public repository set and reports every `uses:` reference: mutable tag or branch against commit sha pin, archived, renamed or missing action repositories, the last release date and a risk class. One dataset row for each refer

- **URL**: https://apify.com/kingii98/github-actions-reference-pin-and-archived-action-inventory.md
- **Developed by:** [kingii98](https://apify.com/kingii98) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 repository auditeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## GitHub Actions Reference Pin and Archived-Action Inventory

Give one table of every `uses:` reference in a set of public GitHub
repositories. The table shows which references are mutable tags or branches,
which point to an archived, renamed or missing Action, when the Action was
last released, and whether the owner account changed.

Use it before a security review, before a customer questionnaire, or as a
weekly CI hygiene run.

The Actor sends HTTP GET calls to the public GitHub REST API and reads the
workflow bodies from `raw.githubusercontent.com`. There is no browser, no
proxy, no database, and no write call. A token is optional.

### What the Actor does

1. It lists `.github/workflows` of each repository.
2. It reads each workflow file and finds every `uses:` value, with its job and
   its step index.
3. It reads each distinct Action repository once: the archive flag, the owner
   account and its type, the default branch, the last release and the last
   push.
4. It resolves the commit sha of each mutable reference, up to the configured
   bound.
5. It applies two rules and writes one row for each reference.

#### The pin rule

A reference passes the pin rule when it names a full 40 character commit sha.
A tag, a branch, a short sha and a `uses:` value without `@` all fail the rule,
because the code behind them can change. A container image passes when it
carries a `@sha256:` digest.

An owner in `trustedOwners` is free of the pin rule. It is not free of the
maintenance rule.

A local reference (`./.github/actions/...`) is reported and never flagged: it
lives in the audited repository itself.

#### The maintenance rule

A reference passes the maintenance rule when the Action repository is active,
and when it had a release or a push inside `staleReleaseDays`. An archived, a
renamed and a missing Action repository all fail the rule.

An Action repository that the Actor could not read (for example because of the
GitHub rate limit) gets the state `unknown`. It is reported, but it is not
flagged and it is not charged.

#### Reference type

GitHub does not say in the reference itself whether a name is a tag or a
branch. The Actor reads the name the way a reviewer reads it: a full or short
hexadecimal name is a `sha`, a version name such as `v4` or `1.2.3` is a `tag`,
the default branch of the Action and the common branch names are a `branch`,
and every other name is a `branch`. A tag and a branch are both mutable, so
this reading does not change the verdict.

### Input

All fields have a default, so a run with an empty input `{}` works.

| Field | Meaning |
| --- | --- |
| `repositories` | 1 to 200 public repositories as `owner/repo`. A GitHub URL is also accepted. |
| `branch` | Branch, tag or commit to read the workflow files from. Empty means the default branch. |
| `trustedOwners` | Owner accounts that may use a mutable reference, for example `actions`. |
| `staleReleaseDays` | An Action without a release or a push inside this many days fails the maintenance rule. Default 365. |
| `maxWorkflowFilesPerRepo` | Highest number of workflow files read in one repository, in name order. Default 3. |
| `resolveCommitSha` | Send one extra call for each mutable reference to resolve the commit sha it points to today. |
| `maxShaResolutions` | Upper bound on those extra calls. Default 5. |
| `maxReferences` | Upper bound on the reference rows of one run. Default 2000. |
| `onlyFlagged` | Write a row only for a flagged reference. The summary always counts every reference. |
| `githubToken` | Optional read-only token. Anonymous callers may send 60 API calls in one hour. |
| `concurrency` | Repositories audited at the same time. Default 4. |
| `requestsPerSecond` | Upper bound on the call rate. Default 5. |
| `timeoutSeconds` | Timeout of one call. Default 15. |
| `maxResponseBytes` | An answer above this size is refused and becomes a reported failure. |

Example:

```json
{
  "repositories": ["actions/checkout", "peter-evans/create-pull-request"],
  "trustedOwners": ["actions"],
  "staleReleaseDays": 365
}
```

### Output

One dataset row for each reference, plus one summary record.

| Field | Meaning |
| --- | --- |
| `repository` | The audited repository. |
| `workflowFile` | The workflow file name, for example `ci.yml`. |
| `workflowPath` | The full path of the workflow file. |
| `jobId` | The job key in the workflow file. |
| `jobName` | The `name:` of the job, when it has one. |
| `stepIndex` | The position of the step inside the job, counted from 0. |
| `stepName` | The `name:` of the step, when it has one. |
| `actionReference` | The `uses:` value as written. |
| `actionRepo` | The Action repository, or `null` for a local or container reference. |
| `referenceType` | `sha`, `tag`, `branch`, `local`, `docker` or `none`. |
| `resolvedCommitSha` | The commit sha the reference points to today. |
| `actionRepoState` | `active`, `archived`, `renamed`, `missing` or `unknown`. |
| `resolvedActionRepo` | The current name of the Action repository after a rename. |
| `actionOwner` | The owner account of the Action. |
| `actionOwnerType` | `Organization` or `User`. |
| `ownerTypeChangeFlag` | True when the Action moved to another owner account. |
| `lastReleaseAt` | The date of the last release of the Action. |
| `lastActivityAt` | The later of the last release and the last push. |
| `daysSinceRelease` | Days since the last release. |
| `daysSinceActivity` | Days since the last release or push. |
| `trustedOwner` | True when the owner is in `trustedOwners`. |
| `pinRulePass` | The verdict of the pin rule. |
| `maintenanceRulePass` | The verdict of the maintenance rule. |
| `flagged` | True when one of the two rules fails. This is the charged finding. |
| `riskClass` | The highest risk of the row, see below. |
| `riskReasons` | Every risk name that applies to the row. |
| `note` | The reason why an Action could not be read. |
| `status` | Summary record: `CLEAN`, `FLAGS_FOUND` or `NO_REPOSITORY_READ`. |
| `checkedAt` | The start time of the run. |

Risk classes, from high to low: `missing-action`, `archived-action`,
`renamed-action`, `unmaintained-action`, `unreadable-reference`,
`unpinned-container-image`, `unpinned-branch`, `mutable-tag`, `short-sha-pin`,
`unverified-action`, `local-action`, `pinned-and-maintained`.

The summary record also carries `repositoriesRequested`,
`repositoriesAudited`, `repositoriesFailed`, `workflowFilesRead`,
`referencesFound`, `referencesFlagged`, `byRiskClass`, `failures`,
`shaResolutions`, `requestsSent`, `truncated` and `rateLimited`.

### Charged events

The Actor is paid for each event:

| Event | Unit | Price |
| --- | --- | --- |
| `repository-audited` | One public repository whose workflow files were read. A repository that could not be read is reported, not charged. | USD 0.02 |
| `action-reference-flagged` | One reference that fails the pin rule or the maintenance rule. An Action with the state `unknown` is not charged. | USD 0.004 |

A clean repository therefore costs one event only. The finding event makes the
charge follow the delivered findings.

### A verdict is not a failure

A failed gate is a result, not a malfunction. Zero findings, a missing
repository, a repository without workflow files, and a run that the GitHub rate
limit cut short all end with a SUCCEEDED run, a dataset record and a status
message. A FAILED run means that the Actor itself broke, or that the input
could not be parsed.

### Limits

- Public repositories only. A private repository answers 404 and is reported.
- Anonymous callers may send 60 GitHub API calls in one hour. Supply
  `githubToken` for a run over more than a few repositories.
- The Actor reads `maxWorkflowFilesPerRepo` files for each repository, in name
  order. Raise it for a complete audit.
- A tag and a branch are told apart by their name, see above.

### Development

```bash
uv sync
uv run pytest
uv run ruff check .
```

# Actor input Schema

## `repositories` (type: `array`):

1 to 200 public repositories in the form "owner/repo". The Actor reads the workflow files of each one and reports every `uses:` reference that they hold.

## `branch` (type: `string`):

Branch, tag or commit to read the workflow files from. Leave this empty to use the default branch of each repository.

## `trustedOwners` (type: `array`):

Owner accounts whose actions may use a mutable tag, for example "actions". A reference to a trusted owner does not fail the pin rule. It still fails the maintenance rule when the action repository is archived, renamed or missing.

## `staleReleaseDays` (type: `integer`):

An action with no release and no push inside this many days fails the maintenance rule.

## `maxWorkflowFilesPerRepo` (type: `integer`):

Highest number of workflow files that the Actor reads in one repository. The files are read in name order.

## `resolveCommitSha` (type: `boolean`):

Send one extra API call for each flagged mutable reference to resolve the commit sha that it points to today. Turn this off to spend fewer API calls.

## `maxShaResolutions` (type: `integer`):

Upper bound on the extra sha resolution calls of one run.

## `maxReferences` (type: `integer`):

Upper bound on the reference rows of one run. The run stops to read more workflow files when it reaches this number.

## `onlyFlagged` (type: `boolean`):

Write a dataset row only for a reference that fails the pin rule or the maintenance rule. The summary always counts every reference.

## `githubToken` (type: `string`):

Optional. The Actor reads public repositories without a token. An anonymous caller may send 60 API calls in one hour, which is enough for a few repositories only. A read-only token lifts the limit to 5000 calls in one hour.

## `concurrency` (type: `integer`):

Highest number of repositories that the Actor audits at the same time.

## `requestsPerSecond` (type: `integer`):

Upper bound on the call rate against the GitHub API.

## `timeoutSeconds` (type: `integer`):

Highest time in seconds that one GitHub API call may take. A call that passes this time is stopped, and the target becomes a reported failure.

## `maxResponseBytes` (type: `integer`):

An answer that passes this size is refused, and the target becomes a reported failure.

## Actor input object example

```json
{
  "repositories": [
    "actions/checkout",
    "peter-evans/create-pull-request"
  ],
  "branch": "",
  "trustedOwners": [],
  "staleReleaseDays": 365,
  "maxWorkflowFilesPerRepo": 3,
  "resolveCommitSha": true,
  "maxShaResolutions": 5,
  "maxReferences": 2000,
  "onlyFlagged": false,
  "concurrency": 4,
  "requestsPerSecond": 5,
  "timeoutSeconds": 15,
  "maxResponseBytes": 2000000
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "repositories": [
        "actions/checkout",
        "peter-evans/create-pull-request"
    ],
    "branch": "",
    "trustedOwners": [],
    "staleReleaseDays": 365,
    "maxWorkflowFilesPerRepo": 3,
    "resolveCommitSha": true,
    "maxShaResolutions": 5,
    "maxReferences": 2000,
    "onlyFlagged": false,
    "concurrency": 4,
    "requestsPerSecond": 5,
    "timeoutSeconds": 15,
    "maxResponseBytes": 2000000
};

// Run the Actor and wait for it to finish
const run = await client.actor("kingii98/github-actions-reference-pin-and-archived-action-inventory").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "repositories": [
        "actions/checkout",
        "peter-evans/create-pull-request",
    ],
    "branch": "",
    "trustedOwners": [],
    "staleReleaseDays": 365,
    "maxWorkflowFilesPerRepo": 3,
    "resolveCommitSha": True,
    "maxShaResolutions": 5,
    "maxReferences": 2000,
    "onlyFlagged": False,
    "concurrency": 4,
    "requestsPerSecond": 5,
    "timeoutSeconds": 15,
    "maxResponseBytes": 2000000,
}

# Run the Actor and wait for it to finish
run = client.actor("kingii98/github-actions-reference-pin-and-archived-action-inventory").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "repositories": [
    "actions/checkout",
    "peter-evans/create-pull-request"
  ],
  "branch": "",
  "trustedOwners": [],
  "staleReleaseDays": 365,
  "maxWorkflowFilesPerRepo": 3,
  "resolveCommitSha": true,
  "maxShaResolutions": 5,
  "maxReferences": 2000,
  "onlyFlagged": false,
  "concurrency": 4,
  "requestsPerSecond": 5,
  "timeoutSeconds": 15,
  "maxResponseBytes": 2000000
}' |
apify call kingii98/github-actions-reference-pin-and-archived-action-inventory --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kingii98/github-actions-reference-pin-and-archived-action-inventory"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/2hBDlCc5QoTWbewfa/builds/7SY7kPAc4Z40dsDAN/openapi.json
