Skip to content

Commit b95dbb9

Browse files
committed
fix: publish action repo releases automatically (#44)
* fix: publish action repo releases automatically Signed-off-by: Michael Kantor <6068672+kantorcodes@users.noreply.github.com> * fix: address action release review feedback Signed-off-by: Michael Kantor <6068672+kantorcodes@users.noreply.github.com> * fix: detect untracked action bundle files Signed-off-by: Michael Kantor <6068672+kantorcodes@users.noreply.github.com> * fix: make action release publication rerunnable Signed-off-by: Michael Kantor <6068672+kantorcodes@users.noreply.github.com> * docs: tighten action marketplace copy Signed-off-by: Michael Kantor <6068672+kantorcodes@users.noreply.github.com> --------- Signed-off-by: Michael Kantor <6068672+kantorcodes@users.noreply.github.com>
1 parent cc8f1af commit b95dbb9

3 files changed

Lines changed: 127 additions & 123 deletions

File tree

Lines changed: 93 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,140 @@
11
name: Publish GitHub Action Repository
22

33
on:
4-
release:
5-
types: [published, edited]
64
workflow_dispatch:
7-
inputs:
8-
release_tag:
9-
description: "Release tag to publish from the source repository"
10-
required: false
11-
default: ""
12-
action_repository:
13-
description: "Dedicated public repository that hosts the Marketplace action"
14-
required: false
15-
default: "hashgraph-online/hol-codex-plugin-scanner-action"
16-
create_repository:
17-
description: "Create the dedicated action repository if it does not exist"
18-
required: false
19-
default: true
20-
type: boolean
5+
push:
6+
branches:
7+
- main
8+
paths:
9+
- action/action.yml
10+
- action/README.md
11+
- LICENSE
12+
- SECURITY.md
13+
- CONTRIBUTING.md
2114

2215
permissions:
2316
contents: read
2417

2518
concurrency:
26-
group: codex-plugin-scanner-action-publish-${{ github.event.release.tag_name || inputs.release_tag || github.ref }}
19+
group: codex-plugin-scanner-action-repo-${{ github.ref }}
2720
cancel-in-progress: false
2821

2922
jobs:
3023
publish-action-repo:
31-
name: Publish GitHub Action Repository
24+
name: Sync action repo + publish release notes
3225
runs-on: ubuntu-latest
26+
permissions:
27+
contents: read
3328
env:
34-
GH_TOKEN: ${{ secrets.ACTION_REPO_TOKEN }}
35-
ACTION_REPOSITORY: ${{ inputs.action_repository != '' && inputs.action_repository || vars.ACTION_REPOSITORY != '' && vars.ACTION_REPOSITORY || 'hashgraph-online/hol-codex-plugin-scanner-action' }}
36-
RELEASE_TAG: ${{ github.event.release.tag_name || inputs.release_tag }}
37-
CREATE_REPOSITORY: ${{ github.event_name == 'workflow_dispatch' && (inputs.create_repository && 'true' || 'false') || 'true' }}
38-
PUBLISH_IMMUTABLE_RELEASE: ${{ github.event_name == 'release' || inputs.release_tag != '' }}
39-
SOURCE_REF: ${{ github.event.release.tag_name || inputs.release_tag || github.ref_name }}
29+
ACTION_REPOSITORY: ${{ vars.ACTION_REPOSITORY != '' && vars.ACTION_REPOSITORY || 'hashgraph-online/hol-codex-plugin-scanner-action' }}
30+
SOURCE_REF: ${{ github.sha }}
4031
SOURCE_REPOSITORY: ${{ github.repository }}
4132
SOURCE_SERVER_URL: ${{ github.server_url }}
4233
steps:
43-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
44-
with:
45-
ref: ${{ github.event.release.tag_name || inputs.release_tag || github.ref }}
46-
fetch-depth: 0
47-
4834
- name: Validate publication credentials
35+
env:
36+
ACTION_REPO_TOKEN: ${{ secrets.ACTION_REPO_TOKEN }}
4937
run: |
50-
if [ -z "${GH_TOKEN}" ]; then
51-
echo "ACTION_REPO_TOKEN is required to publish the GitHub Action repository." >&2
38+
if [ -z "$ACTION_REPO_TOKEN" ]; then
39+
echo "ACTION_REPO_TOKEN must be configured to publish the Marketplace action repository." >&2
5240
exit 1
5341
fi
5442
55-
- name: Resolve version
56-
id: version
57-
run: |
58-
TAG="${RELEASE_TAG}"
59-
if [ -z "${TAG}" ]; then
60-
TAG=$(python3 -c "import tomllib; p=tomllib.load(open('pyproject.toml','rb')); print('v' + p['project']['version'])")
61-
fi
62-
VERSION="${TAG#v}"
63-
echo "tag=${TAG}" >> "${GITHUB_OUTPUT}"
64-
echo "version=${VERSION}" >> "${GITHUB_OUTPUT}"
43+
- name: Checkout source repository
44+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
6545

66-
- name: Ensure action repository exists
67-
run: |
68-
if gh repo view "${ACTION_REPOSITORY}" >/dev/null 2>&1; then
69-
exit 0
70-
fi
71-
if [ "${CREATE_REPOSITORY}" != "true" ]; then
72-
echo "Action repository ${ACTION_REPOSITORY} does not exist and automatic creation is disabled." >&2
73-
exit 1
74-
fi
75-
gh repo create "${ACTION_REPOSITORY}" \
76-
--public \
77-
--description "HOL Codex Plugin Scanner GitHub Action" \
78-
--homepage "${SOURCE_SERVER_URL}/${SOURCE_REPOSITORY}" \
79-
--disable-wiki
80-
81-
- name: Sync action repository contents
46+
- name: Compute next action release tag
47+
id: version
48+
env:
49+
GH_TOKEN: ${{ secrets.ACTION_REPO_TOKEN }}
8250
run: |
83-
VERSION="${{ steps.version.outputs.version }}"
84-
TAG="${{ steps.version.outputs.tag }}"
85-
WORKDIR="${RUNNER_TEMP}/action-repository"
86-
git clone "https://x-access-token:${GH_TOKEN}@github.com/${ACTION_REPOSITORY}.git" "${WORKDIR}"
87-
cd "${WORKDIR}"
51+
LAST_TAG=$(gh release list --repo "$ACTION_REPOSITORY" --limit 1 --json tagName --jq '.[0].tagName // ""')
8852
89-
if git ls-remote --exit-code origin refs/heads/main >/dev/null 2>&1; then
90-
git fetch origin main
91-
git switch -C main origin/main
53+
if [ -z "$LAST_TAG" ]; then
54+
TAG="v1.0.0"
9255
else
93-
git switch --orphan main
56+
IFS=. read -r MAJOR MINOR PATCH <<< "${LAST_TAG#v}"
57+
if [ -z "$MAJOR" ] || [ -z "$MINOR" ] || [ -z "$PATCH" ]; then
58+
echo "Unsupported release tag format: $LAST_TAG" >&2
59+
exit 1
60+
fi
61+
TAG="v${MAJOR}.${MINOR}.$((PATCH + 1))"
9462
fi
9563
96-
find . -mindepth 1 -maxdepth 1 ! -name '.git' -exec rm -rf {} +
64+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
65+
66+
- name: Clone action repository
67+
env:
68+
GH_TOKEN: ${{ secrets.ACTION_REPO_TOKEN }}
69+
run: gh repo clone "$ACTION_REPOSITORY" action-repo -- --depth 1
9770

71+
- name: Sync root-ready action bundle
72+
working-directory: action-repo
73+
run: |
9874
cp "${GITHUB_WORKSPACE}/action/action.yml" action.yml
9975
cp "${GITHUB_WORKSPACE}/action/README.md" README.md
10076
cp "${GITHUB_WORKSPACE}/LICENSE" LICENSE
10177
cp "${GITHUB_WORKSPACE}/SECURITY.md" SECURITY.md
78+
cp "${GITHUB_WORKSPACE}/CONTRIBUTING.md" CONTRIBUTING.md
10279
103-
git config user.name "github-actions[bot]"
104-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
105-
git add action.yml README.md LICENSE SECURITY.md
106-
107-
HAS_HEAD=true
108-
git rev-parse --verify HEAD >/dev/null 2>&1 || HAS_HEAD=false
109-
110-
if [ "${HAS_HEAD}" = "true" ] && git diff --cached --quiet; then
111-
echo "No action repository content changes detected."
80+
- name: Detect sync changes
81+
id: diff
82+
working-directory: action-repo
83+
run: |
84+
if [ -n "$(git status --short -- action.yml README.md LICENSE SECURITY.md CONTRIBUTING.md)" ]; then
85+
echo "changed=true" >> "$GITHUB_OUTPUT"
11286
else
113-
git commit -m "chore: publish action bundle ${TAG}"
114-
git push origin HEAD:main
87+
echo "changed=false" >> "$GITHUB_OUTPUT"
11588
fi
11689
117-
git tag -f v1
118-
git push origin refs/tags/v1 --force
90+
- name: Commit synced bundle
91+
if: steps.diff.outputs.changed == 'true'
92+
working-directory: action-repo
93+
run: |
94+
git config user.name "github-actions[bot]"
95+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
96+
git add action.yml README.md LICENSE SECURITY.md CONTRIBUTING.md
97+
git commit -m "chore: publish action bundle ${{ steps.version.outputs.tag }}"
11998
120-
if [ "${PUBLISH_IMMUTABLE_RELEASE}" = "true" ]; then
121-
git tag -f "${TAG}"
122-
git push origin "refs/tags/${TAG}" --force
123-
fi
99+
- name: Push action repository branch and tags
100+
if: steps.diff.outputs.changed == 'true'
101+
working-directory: action-repo
102+
run: |
103+
TAG="${{ steps.version.outputs.tag }}"
104+
git push origin HEAD:main
105+
git tag "$TAG"
106+
git push origin "refs/tags/${TAG}"
107+
git tag -fa v1 -m "Update floating major tag to ${TAG}"
108+
git push origin refs/tags/v1 --force
124109
125-
- name: Create or update action repository release
126-
if: env.PUBLISH_IMMUTABLE_RELEASE == 'true'
110+
- name: Check action release state
111+
id: release_state
112+
working-directory: action-repo
113+
env:
114+
GH_TOKEN: ${{ secrets.ACTION_REPO_TOKEN }}
127115
run: |
128-
VERSION="${{ steps.version.outputs.version }}"
129116
TAG="${{ steps.version.outputs.tag }}"
130-
if [ -n "${RELEASE_TAG}" ]; then
131-
NOTES="Published automatically from ${SOURCE_SERVER_URL}/${SOURCE_REPOSITORY}/releases/tag/${TAG}"
117+
118+
if gh release view "${TAG}" --repo "$ACTION_REPOSITORY" >/dev/null 2>&1; then
119+
echo "release_exists=true" >> "$GITHUB_OUTPUT"
132120
else
133-
NOTES="Published automatically from ${SOURCE_SERVER_URL}/${SOURCE_REPOSITORY}/tree/${SOURCE_REF}"
121+
echo "release_exists=false" >> "$GITHUB_OUTPUT"
134122
fi
135123
136-
if gh release view "${TAG}" --repo "${ACTION_REPOSITORY}" >/dev/null 2>&1; then
137-
gh release edit "${TAG}" \
138-
--repo "${ACTION_REPOSITORY}" \
139-
--title "${TAG}" \
140-
--notes "${NOTES}"
124+
if git ls-remote --tags origin "refs/tags/${TAG}" | grep -q .; then
125+
echo "tag_exists=true" >> "$GITHUB_OUTPUT"
141126
else
142-
gh release create "${TAG}" \
143-
--repo "${ACTION_REPOSITORY}" \
144-
--title "${TAG}" \
145-
--notes "${NOTES}"
127+
echo "tag_exists=false" >> "$GITHUB_OUTPUT"
146128
fi
129+
130+
- name: Create action repository release
131+
if: steps.release_state.outputs.release_exists == 'false' && (steps.diff.outputs.changed == 'true' || steps.release_state.outputs.tag_exists == 'true')
132+
env:
133+
GH_TOKEN: ${{ secrets.ACTION_REPO_TOKEN }}
134+
run: |
135+
TAG="${{ steps.version.outputs.tag }}"
136+
gh release create "${TAG}" \
137+
--repo "$ACTION_REPOSITORY" \
138+
--title "$TAG" \
139+
--generate-notes \
140+
--notes "Published automatically from ${SOURCE_SERVER_URL}/${SOURCE_REPOSITORY}/tree/${SOURCE_REF}"

action/README.md

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
# HOL Codex Plugin Scanner GitHub Action
22

3-
Scan your [Codex plugin](https://developers.openai.com/codex/plugins) for security, publishability, and best practices. The action emits a `0-100` score, a grade, and the requested report format.
3+
[![Latest Release](https://img.shields.io/github/v/release/hashgraph-online/hol-codex-plugin-scanner-action?display_name=tag)](https://github.com/hashgraph-online/hol-codex-plugin-scanner-action/releases/latest)
4+
[![Marketplace Repository](https://img.shields.io/badge/github-marketplace_repo-0A84FF)](https://github.com/hashgraph-online/hol-codex-plugin-scanner-action)
5+
[![Source of Truth](https://img.shields.io/badge/source-codex--plugin--scanner-111827)](https://github.com/hashgraph-online/codex-plugin-scanner/tree/main/action)
6+
[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/hashgraph-online/codex-plugin-scanner/blob/main/LICENSE)
47

5-
This README is intentionally root-ready for a dedicated GitHub Marketplace action repository. GitHub Marketplace requires that repository to contain a single root `action.yml` and no workflow files.
8+
| ![Hashgraph Online Logo](https://raw.githubusercontent.com/hashgraph-online/standards-sdk-py/main/Hashgraph-Online.png) | Marketplace-ready GitHub Action for scanning [Codex plugins](https://developers.openai.com/codex/plugins) for security, publishability, runtime readiness, and registry trust signals. The action emits structured reports, SARIF, policy results, and submission metadata while staying aligned to the main scanner release train.<br><br>[Latest Release](https://github.com/hashgraph-online/hol-codex-plugin-scanner-action/releases/latest)<br>[Marketplace Repository](https://github.com/hashgraph-online/hol-codex-plugin-scanner-action)<br>[Scanner Source of Truth](https://github.com/hashgraph-online/codex-plugin-scanner/tree/main/action)<br>[Report an Issue](https://github.com/hashgraph-online/codex-plugin-scanner/issues) |
9+
| :--- | :--- |
10+
11+
This repository is the Marketplace-facing wrapper for the scanner action. The main scanner repo remains the source of truth, while this published action bundle keeps the required root `action.yml` layout for GitHub Marketplace.
612

713
## Usage
814

@@ -15,8 +21,6 @@ This README is intentionally root-ready for a dedicated GitHub Marketplace actio
1521
fail_on_severity: high
1622
```
1723
18-
If your repository exposes multiple plugins from `.agents/plugins/marketplace.json`, keep `plugin_dir: "."`. The action will discover local `./plugins/...` entries automatically, scan each local plugin, and skip remote marketplace entries.
19-
2024
## Inputs
2125
2226
| Input | Description | Default |
@@ -37,7 +41,7 @@ If your repository exposes multiple plugins from `.agents/plugins/marketplace.js
3741
| `fail_on_severity` | Fail on findings at or above this severity: `none`, `critical`, `high`, `medium`, `low`, `info` | `none` |
3842
| `cisco_skill_scan` | Cisco skill-scanner mode: `auto`, `on`, `off` | `auto` |
3943
| `cisco_policy` | Cisco policy preset: `permissive`, `balanced`, `strict` | `balanced` |
40-
| `install_cisco` | Install the scanner with its `cisco` extra enabled | `false` |
44+
| `install_cisco` | Install the published Cisco skill-scanner dependency used by this repo | `false` |
4145
| `submission_enabled` | Open submission issues for awesome-list and registry automation when the plugin clears the submission threshold | `false` |
4246
| `submission_score_threshold` | Minimum score required before a submission issue is created | `80` |
4347
| `submission_repos` | Comma-separated GitHub repositories that should receive the submission issue | `hashgraph-online/awesome-codex-plugins` |
@@ -97,7 +101,7 @@ jobs:
97101
scan:
98102
runs-on: ubuntu-latest
99103
steps:
100-
- uses: actions/checkout@v6
104+
- uses: actions/checkout@v4
101105
- uses: hashgraph-online/hol-codex-plugin-scanner-action@v1
102106
with:
103107
plugin_dir: "."
@@ -119,7 +123,6 @@ This `plugin_dir: "."` pattern is correct for both single-plugin repositories an
119123
cisco_policy: strict
120124
install_cisco: true
121125
```
122-
The action installs the scanner with its published `cisco` extra enabled, so the optional Cisco analysis path stays aligned with the dependency declared in `pyproject.toml`.
123126

124127
### Export registry payload for Codex ecosystem automation
125128

@@ -153,7 +156,7 @@ jobs:
153156
scan:
154157
runs-on: ubuntu-latest
155158
steps:
156-
- uses: actions/checkout@v6
159+
- uses: actions/checkout@v4
157160
158161
- name: Scan plugin and submit if eligible
159162
id: scan
@@ -184,7 +187,7 @@ Use a fine-grained token with `issues:write` on `hashgraph-online/awesome-codex-
184187
output: scan-report.md
185188
186189
- name: Comment PR
187-
uses: actions/github-script@v8
190+
uses: actions/github-script@v7
188191
with:
189192
script: |
190193
const fs = require('fs');
@@ -199,16 +202,18 @@ Use a fine-grained token with `issues:write` on `hashgraph-online/awesome-codex-
199202

200203
## Release Management
201204

202-
- Publish immutable releases such as `v1.4.0`.
205+
- Publish immutable releases for this Marketplace wrapper repository automatically from the source scanner repo when `action/` changes merge to `main`.
203206
- Move the floating major tag `v1` to the latest compatible release.
204207
- Keep this action in its own public repository for GitHub Marketplace publication.
205-
- Configure `ACTION_REPO_TOKEN` in the source repository so `publish-action-repo.yml` can sync this root-ready bundle automatically.
208+
- Configure `ACTION_REPO_TOKEN` as a secret in the source repository so `publish-action-repo.yml` can automatically sync this root-ready bundle, create the action-repo release, and publish autogenerated release notes.
206209
- Optionally set `ACTION_REPOSITORY` in the source repository if the target repository should not be `hashgraph-online/hol-codex-plugin-scanner-action`.
207210

208-
## Source Of Truth
211+
## Source of Truth
209212

210213
The source bundle for this action lives in the main scanner repository under `action/`. Release artifacts from that repository should export a root-ready action bundle for the dedicated Marketplace repository.
211214

215+
Direct edits in this Marketplace repository should stay limited to Marketplace-specific copy or metadata. Functional changes and release publication logic belong in `hashgraph-online/codex-plugin-scanner` so merges there can publish a matching action release automatically.
216+
212217
## License
213218

214219
[Apache-2.0](https://github.com/hashgraph-online/codex-plugin-scanner/blob/main/LICENSE)

tests/test_action_bundle.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ def test_action_metadata_includes_marketplace_branding_and_fallback_install() ->
1515
assert "actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405" in action_text
1616
assert "pip install codex-plugin-scanner" in action_text
1717
assert 'pip install "$LOCAL_SOURCE"' in action_text
18-
assert 'pip install "$LOCAL_SOURCE[cisco]"' in action_text
19-
assert 'pip install "codex-plugin-scanner[cisco]"' in action_text
2018
assert "write_step_summary:" in action_text
2119
assert "profile:" in action_text
2220
assert "config:" in action_text
@@ -80,32 +78,39 @@ def test_publish_action_repo_workflow_syncs_action_repository() -> None:
8078
assert "hashgraph-online/hol-codex-plugin-scanner-action" in workflow_text
8179
assert "Validate publication credentials" in workflow_text
8280
assert "if: secrets.ACTION_REPO_TOKEN != ''" not in workflow_text
83-
assert "inputs.create_repository && 'true' || 'false'" in workflow_text
81+
assert 'git status --short -- action.yml README.md LICENSE SECURITY.md CONTRIBUTING.md' in workflow_text
8482
assert "SOURCE_REF" in workflow_text
85-
assert "PUBLISH_IMMUTABLE_RELEASE" in workflow_text
86-
assert 'gh repo create "${ACTION_REPOSITORY}"' in workflow_text
83+
assert 'gh repo clone "$ACTION_REPOSITORY" action-repo -- --depth 1' in workflow_text
8784
assert 'cp "${GITHUB_WORKSPACE}/action/action.yml" action.yml' in workflow_text
88-
assert 'if [ "${PUBLISH_IMMUTABLE_RELEASE}" = "true" ]; then' in workflow_text
85+
assert 'git push origin HEAD:main' in workflow_text
86+
assert 'gh release view "${TAG}" --repo "$ACTION_REPOSITORY"' in workflow_text
87+
assert 'git ls-remote --tags origin "refs/tags/${TAG}"' in workflow_text
8988
assert "git push origin refs/tags/v1 --force" in workflow_text
89+
assert "steps.release_state.outputs.release_exists == 'false'" in workflow_text
90+
assert "steps.release_state.outputs.tag_exists == 'true'" in workflow_text
9091
assert 'gh release create "${TAG}"' in workflow_text
92+
assert "--generate-notes" in workflow_text
9193
assert "Published automatically from ${SOURCE_SERVER_URL}/${SOURCE_REPOSITORY}/tree/${SOURCE_REF}" in workflow_text
9294

9395

9496
def test_action_bundle_docs_live_in_action_readme() -> None:
9597
action_readme = (ROOT / "action" / "README.md").read_text(encoding="utf-8")
9698

97-
assert "single root `action.yml`" in action_readme
98-
assert "no workflow files" in action_readme
99-
assert "dedicated Marketplace repository" in action_readme
100-
assert "Source Of Truth" in action_readme
99+
assert "Hashgraph-Online.png" in action_readme
100+
assert "Latest Release" in action_readme
101+
assert "Marketplace-facing wrapper" in action_readme
102+
assert "root `action.yml` layout" in action_readme
103+
assert "published action bundle" in action_readme
104+
assert "Source of Truth" in action_readme
101105
assert "registry_payload_output" in action_readme
102106
assert "grade_label" in action_readme
103107
assert "max_severity" in action_readme
104108
assert "submission issue" in action_readme
105109
assert "awesome-codex-plugins" in action_readme
106110
assert "publish-action-repo.yml" in action_readme
107-
assert "actions/github-script@v8" in action_readme
108-
assert "scanner with its published `cisco` extra enabled" in action_readme
111+
assert "hashgraph-online/hol-codex-plugin-scanner-action@v1" in action_readme
112+
assert "actions/checkout@v4" in action_readme
113+
assert "actions/github-script@v7" in action_readme
109114

110115

111116
def test_readme_uses_stable_apache_license_badge() -> None:

0 commit comments

Comments
 (0)