Extract the model profile refresh logic into a reusable `workflow_call`
workflow so external repos like `langchain-google` and `langchain-aws`
can run the same daily profile refresh and get auto-PRs without
duplicating the pipeline. The in-monorepo caller becomes a thin wrapper
passing provider JSON.
## Changes
- Add `_refresh_model_profiles.yml` as a reusable `workflow_call`
workflow — accepts a `providers` JSON array of `{provider, data_dir}`
pairs, optional `cli-path` (skips cloning the CLI repo when the caller
already has it), and configurable PR metadata inputs
- External callers get the `langchain-profiles` CLI via sparse checkout
of `langchain-ai/langchain` at a configurable `cli-ref`; the in-monorepo
caller short-circuits with `cli-path: libs/model-profiles`
- Add input validation step using `jq` — rejects non-array JSON and
entries missing `provider`/`data_dir` keys with `::error::` annotations
- Replace the piped `while read` loop with `mapfile`/`for` +
per-provider error handling: one provider failure no longer kills the
rest, and all failures are collected and reported at the end
- Route all `${{ inputs.* }}` expressions through `env:` bindings in
`run:` blocks to prevent script injection from caller-controlled values
- Validate `cli-path` existence before use, with a clear error if the
directory is missing
- Summary step now runs with `if: always()` and handles
failure/success/no-op states separately
- Refactor `refresh_model_profiles.yml` into a thin caller that passes
the 10 in-monorepo providers as JSON
Extract additional fields from models.dev into `_model_data_to_profile`:
`name`, `status`, `release_date`, `last_updated`, `open_weights`,
`attachment`, `temperature`
Move the model profile refresh logic from an inline bash script in the
GitHub Actions workflow into a `make refresh-profiles` target in
`libs/model-profiles/Makefile`. This makes it runnable locally with a
single command and keeps the provider map in one place instead of
duplicated between CI and developer docs.
## Summary
- Adds top-level `permissions: contents: read` to 5 workflows that only
had job-level permissions: `pr_labeler_file`, `pr_labeler_title`,
`tag-external-contributions`, `v03_api_doc_build`,
`auto-label-by-package`
- SHA-pins all 14 third-party actions to full commit SHAs to prevent
supply chain attacks via tag hijacking
## Why
**Missing top-level permissions:** Without an explicit top-level
`permissions` block, workflows inherit the repository/org default token
permissions, which may be overly broad. Adding `contents: read` as the
default restricts the blast radius if a dependency or action step is
compromised.
**SHA pinning:** Mutable tags (`@v1`, `@master`) can be force-pushed by
the action maintainer or an attacker who compromises their account.
Pinning to a full 40-character SHA ensures the exact reviewed code
always runs. Tag comments are preserved for readability.
### Actions pinned
| Action | File(s) |
|--------|---------|
| `pypa/gh-action-pypi-publish` | `_release.yml` (2 uses) |
| `ncipollo/release-action` | `_release.yml` |
| `Ana06/get-changed-files` | `check_diffs.yml` |
| `astral-sh/setup-uv` | `check_diffs.yml`, `uv_setup/action.yml` |
| `CodSpeedHQ/action` | `check_diffs.yml` |
| `google-github-actions/auth` | `integration_tests.yml` |
| `aws-actions/configure-aws-credentials` | `integration_tests.yml` |
| `amannn/action-semantic-pull-request` | `pr_lint.yml` |
| `bcoe/conventional-release-labels` | `pr_labeler_title.yml` |
| `mikefarah/yq` | `v03_api_doc_build.yml` |
| `EndBug/add-and-commit` | `v03_api_doc_build.yml` |
| `peter-evans/create-pull-request` | `refresh_model_profiles.yml` |
## Test plan
- [x] CI passes — all workflows still resolve their actions correctly
- [x] Verify no functional change: SHA refs point to the same code as
the previous tags
---
> This PR was generated with assistance from an AI coding agent as part
of a repository posture check.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Schedules the `refresh_model_profiles` workflow to run daily at 08:00
UTC (manual trigger available).
- Adds a job summary step that reports whether a PR was created/updated
or skipped because profiles were already up to date.
- Each run supersedes any stale PR from a previous run since the action
force-pushes to a fixed branch (`bot/refresh-model-profiles`).