Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Pah Sync

ASecurity

Populate this environment's Private Automation Hub as code — all Red Hat certified and validated collections windowed to their 3 newest versions, plus the ericcames and mlowcher61 community namespaces at their current version only. Regenerates the pinned lists from upstream, runs playbooks/sync_hub.yml, then asks the hub what actually landed. TRIGGER when: the user asks to populate, sync, fill or refresh Private Automation Hub or PAH, wants certified or validated collections in their hub, ask...

2 stars
0 votes
0 copies
0 views
Added 10/6/2026
devopspythonbashawsdebugginggit

Works with

climcp

Security Analysis

A100/100

Scanned 10/6/2026

$npx -y skills add ericcames/sales.demos --skill pah-sync --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Pah Sync?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Pah Sync
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/ericcames-pah-sync/badge)](https://www.skillsdirectory.com/skills/ericcames-pah-sync)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
name: pah-sync
description: "Populate this environment's Private Automation Hub as code — all Red Hat certified and validated collections windowed to their 3 newest versions, plus the ericcames and mlowcher61 community namespaces at their current version only. Regenerates the pinned lists from upstream, runs playbooks/sync_hub.yml, then asks the hub what actually landed. TRIGGER when: the user asks to populate, sync, fill or refresh Private Automation Hub or PAH, wants certified or validated collections in their hub, asks why a hub is empty or a collection is missing from it, wants to refresh hub/*-requirements.yml, or hits a hub sync that reports success with no content. SKIP: if the user wants to point AAP projects AT the hub via organization Galaxy credentials — that is pah-link-aap — or wants to mirror the execution environment image, which is sales-demos-ee-build."
---

# pah-sync

Populates the Private Automation Hub that ships with every AAP environment,
from configuration in git rather than from the web UI.

This skill contains **no logic**. The work is
[`playbooks/sync_hub.yml`](../../../playbooks/sync_hub.yml) and
[`utilities/refresh-hub-requirements.py`](../../../utilities/refresh-hub-requirements.py).

**Every build already does most of this.** `config.yml` — stage 2 of
`setup.yml` — applies the same remotes and repositories and *starts* a sync
without waiting. What this skill adds is waiting for it and then proving it.

## What lands in the hub

| Repository | Content | Versions |
|---|---|---|
| `rh-certified` | every Red Hat certified collection (214) | 3 newest of each |
| `validated` | every Red Hat validated collection (47) | 3 newest of each |
| `community` | the `ericcames` and `mlowcher61` namespaces (15) | current only |

The lists are committed: `hub/certified-requirements.yml`,
`hub/validated-requirements.yml`, `hub/community-requirements.yml`.

**Pulp has no "keep N versions" control**, which is why those files exist. A
requirements entry of a bare `namespace.name` syncs *every* published version,
and some certified collections have 40+. Each entry instead carries an explicit
version or a `>=` floor. `retain_repo_versions` is not a substitute — it prunes
repository snapshots, not collection versions.

## This is laptop-only, and there is no job template

Unlike every phase of the OpenShift Virtualization demo, this has one entry
point. The Red Hat offline token lives in `~/.ansible.cfg` and nowhere else, and
an execution environment has no such file.

A vaulted fallback was built and verified working, then removed on purpose: it
bought one job template at the cost of a second copy of a rotating credential.
`config.yml` has always been in the same position — you cannot use AAP to
bootstrap itself.

**Never suggest running this from AAP.** It will not error; it will configure the
remotes with an empty token, sync anonymously, and leave the hub empty behind a
green run.

## Preflight Check

```bash
./utilities/preflight.sh "${ENV:-sandbox}" --hub-token

# The three generated lists exist and parse
python3 - <<'PY'
import yaml, pathlib
for k in ("certified", "validated", "community"):
    p = pathlib.Path("hub") / f"{k}-requirements.yml"
    try:
        n = len(yaml.safe_load(p.read_text())["collections"])
        print(f"✅ {p} — {n} collections")
    except Exception as e:
        print(f"❌ {p} — {e}; run utilities/refresh-hub-requirements.py")
PY
```

If any check fails, stop and tell the user which one and the fix shown beside
it. Do not attempt the run with a failing prerequisite.

## Refreshing the pinned lists

Only when you want to pick up new upstream releases. The sync itself does not
need it.

```bash
python3 utilities/refresh-hub-requirements.py
git diff hub/
```

Takes about 25 seconds for all three. Review the diff and commit it — that diff
*is* the record of what changed upstream, and it is the strongest artifact this
use case produces.

Two other modes:

```bash
python3 utilities/refresh-hub-requirements.py --check       # drift only, writes nothing
python3 utilities/refresh-hub-requirements.py --audit-pins  # offline, no network
```

`--audit-pins` answers a question [#69](https://github.com/ericcames/sales.demos/issues/69)
depends on: would every collection in `collections/requirements.yml` actually
resolve from the hub? All nine now do — `ansible.controller` and
`ansible.platform` were pinned below their version window until the generator
started lowering a floor to any version this repo pins.

**It is not the whole question, and #69 found out the hard way.** The audit
checks the collections this repo *names*; a project sync resolves the ones it
*depends on*. Use `--write-approved` for that — it computes the transitive
closure and refuses to write a set missing one.

## Run

```bash
mkdir -p ~/ansible-logs
export ANSIBLE_LOG_PATH=~/ansible-logs/pah-sync-sandbox-$(date +%F-%H%M).log

./utilities/run-ansible.sh playbooks/sync_hub.yml -i inventory --limit sandbox \
  -e target_env=sandbox \
  --vault-id sales.demos@~/secrets/.vault_pass_sales_demos
```

> **Always set `ANSIBLE_LOG_PATH`.** A sync produces a lot of output and you will
> want it after the fact.
>
> **Never pipe the run through `tee`.** It breaks the callback's output handling.
>
> **`--limit` is mandatory.** Without it the run targets both environments, and
> the guard in `tasks/assert_target_environment.yml` fails it.

**Measured 4.4 minutes** (264s) for all three repositories on sandbox — 214
certified, 47 validated, 15 community. Treat that as a floor: that run followed
earlier failed attempts so Pulp may have had artifacts cached, and a genuinely
cold cluster on a slow link will take longer. The playbook allows 90 minutes.

### Useful knobs

```bash
# Configure the remotes and repositories, start no sync at all
-e hub_sync_enabled_override=false

# Start the sync but do not wait for it (what config.yml does)
-e hub_sync_wait_override=false
```

Run `playbooks/validate.yml --check` first if you only want to see what would
change — it is `config.yml` in check mode, and it explicitly forces
`hub_sync_enabled: false` so it cannot start a sync. **`--check` is required and
the play refuses without it** (#173).

**That guard is load-bearing, so do not remove it.** `ansible.hub` 1.1.0's
`collection_repository_sync` checks for check mode with a parameter that is not
in its argument_spec, so the check never fires and the sync runs for real. The
group_vars guard uses `not ansible_check_mode`, which is only True for a CLI
`--check` — a play-level `check_mode: true` leaves it False. Now that
`validate.yml` requires `--check` (#173) the group_vars guard does cover it, but
keep the explicit one anyway: it costs nothing.

Note also that **check mode cannot validate content**: `uri` does not run under
`--check`, so `sync_hub.yml` skips its verification block entirely there.

## Verify it in the EE before merging a change

See `/sales-demos-verify-ee` for why and how. The one command:

```bash
utilities/run-in-ee.sh --with-hub-token playbooks/sync_hub.yml \
  -i inventory --limit sandbox -e target_env=sandbox \
  --vault-id sales.demos@~/secrets/.vault_pass_sales_demos
```

## Curating the `approved` repository

The three synced repositories are mirrors and **a sync cannot remove anything**.
The fourth repository, `approved`, has no remote — its contents are reconciled
from `hub/approved-collections.yml`, and that reconcile adds *and* removes.

```bash
# Regenerate the curated list from this repo's pins (no network)
python3 utilities/refresh-hub-requirements.py --write-approved

# Make the repository equal the file
./utilities/run-ansible.sh playbooks/curate_hub.yml -i inventory --limit sandbox \
  -e target_env=sandbox \
  --vault-id sales.demos@~/secrets/.vault_pass_sales_demos
```

Seeded with the collections in `collections/requirements.yml` at their exact
pinned versions **plus everything those depend on** — the dependency closure, not
the pin list. That distinction is what makes
[#69](https://github.com/ericcames/sales.demos/issues/69) safe, and it was found
by a failure: `approved` held the nine names and a project sync needed
`ansible.eda` too.

**If it refuses with "not present anywhere in this hub"**, that version is
missing — usually because it sits below the certified version window. Run
`--audit-pins`, then regenerate and re-sync: the generator lowers a floor to any
version this repo has pinned.

**This is the repository to point consumers at**, not the mirrors. Doing so is
[`/pah-link-aap`](../pah-link-aap/SKILL.md) — a separate, reversible step,
because a Galaxy credential on the organization makes every project sync depend
on this repository being complete.

## Verify against the hub, not the recap

**A green playbook run is not proof.** The playbook already asserts all of this
when it waits, but run it by hand if you are debugging:

```
mcp__aap-<env>__ansible_content_collections_index
# Returns the collection index for each content repository.
# Check rh-certified (~214), validated (~47), community (15).
```

Three things must hold, and the third is the one people skip:

1. **Every repository is non-empty.** ~214 in `rh-certified`, ~47 in `validated`,
   15 in `community`.
2. **Each community collection is present at exactly its pinned version and no
   other.** Presence alone proves nothing — a full-history sync would also pass
   an existence check.
3. **The certified window bounded the sync.** Count versions of a long-lived
   collection like `amazon.aws` or `redhat.rhel_system_roles`: three, not thirty.
   **Without this there is no evidence the window did anything** — an unwindowed
   sync produces an equally green run and an equally populated hub.

## If it fails

| Symptom | Cause | Fix |
|---|---|---|
| `Red Hat SSO rejected the offline token (invalid_grant: Token is not active)` | The offline token expired. Checked against Red Hat SSO before any remote is rewritten (#597) | Regenerate at `https://console.redhat.com/ansible/automation-hub/token`, replace it in **both** `[galaxy_server.rh_certified]` and `[galaxy_server.rh_validated]`, confirm with `bash utilities/check-hub-token.sh` |
| `N of M hub sync tasks started since ... did not complete` | A sync this run started failed inside Pulp. On an already-populated hub the content counts alone would still pass (#597) | The assert prints each task's error. A 400 from `sso.redhat.com` means the token stopped working mid-run |
| Run is green, repositories are **empty** | #68 recorded this as an expired token that authenticates and returns nothing. Since #597 an expired token is stopped before the sync, so this now means a **live** token that returned nothing | Check the account's Automation Hub access at `https://console.redhat.com/ansible/automation-hub`, and that `hub/*-requirements.yml` name real collections |
| `The Red Hat offline token did not resolve` | No token in `~/.ansible.cfg`, or you are running inside an execution environment | Add the token. If in an EE, stop — this playbook is laptop-only |
| A repository has **thousands** of versions | The remote lost its `requirements_file` | Check `inventory/group_vars/aap/hub_collection_remotes.yml`, re-apply |
| `hub/<x>-requirements.yml is missing or empty` | Never generated, or a failed refresh | `python3 utilities/refresh-hub-requirements.py` |
| Repository created but stays empty, no error | The role gate. The item needs `sync: true` | `-e hub_sync_enabled_override=true`, and check `hub_collection_repositories.yml` |
| A community collection installs but its dependencies do not | `sync_dependencies: false` on the community remote, deliberately | Add the dependency to `hub/community-requirements.yml`, or accept it |
| `Attempting to decrypt but no vault secrets found` | `--vault-id` missing | Add it; see the Run block |

Never paste a live cluster hostname, password or token into a commit message,
issue, or PR. This repo is public.

Attribution

ericcamesericcames
View sourceSee grades on GitHubMore from ericcames →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Terraform Module Library

Build reusable Terraform modules for AWS, Azure, and GCP infrastructure following infrastructure-as-code best practices. Use when creating infrastructure modules, standardizing cloud provisioning, or implementing reusable IaC components.

401991 votes

sematext-otel

Wire a service's OpenTelemetry output to Sematext Cloud. Walks through region, App-type, instrumentation flow (managed OTLP endpoint vs Sematext Agent), and signal selection (traces/metrics/logs), then produces the exact env-var block and points at a runnable reference example in this repo. Invoke when instrumenting a new app for Sematext.

01 votes

Deployment Patterns

Deployment workflows, CI/CD pipeline patterns, Docker containerization, health checks, rollback strategies, and production readiness checklists for web applications. Use when setting up deployment infrastructure or planning releases.

2699140 votes

Babysit

Watch a pull request or review cycle until it is ready to merge. Use when asked to babysit, monitor, or keep checking PR comments, reviews, and CI until all actionable issues are resolved.

971540 votes

V7 Roster

Interact with the Paperclip control plane API for task coordination and governance. Use when checking assignments, updating issue status, posting comments, delegating work, managing routines, or calling Paperclip API endpoints.

953190 votes
View all in devops →