Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,6 @@ jobs:

- name: Print Outputs
run: |
echo "Cache endpoint: ${{ steps.test-action.outputs.cache-endpoint }}"
echo "Cache address: ${{ steps.test-action.outputs.cache-address }}"
echo "Cache socket: ${{ steps.test-action.outputs.cache-socket }}"
echo "Version: ${{ steps.test-action.outputs.version }}"
1 change: 1 addition & 0 deletions .github/workflows/linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ jobs:
env:
CHECKOV_FILE_NAME: .checkov.yml
DEFAULT_BRANCH: main
DOTENV_LINTER_IGNORE_CHECKS: IncorrectDelimiter
FILTER_REGEX_EXCLUDE: dist/**/*
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
LINTER_RULES_PATH: .
Expand Down
30 changes: 14 additions & 16 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

## Project Structure & Module Organization

- `src/`: Action source (ESM). Entry points are `src/main.js` (main) and
`src/post.js` (post-run), bundled to `dist/`.
- `src/`: Action source (ESM). Entry points are `src/index.js` (main) and
`src/post-index.js` (post-run), which invoke `src/main.js` and `src/post.js`,
bundled to `dist/`.
- `dist/`: Built artifacts referenced by `action.yml` (`dist/index.js`,
`dist/post.js`). Keep this in sync with source changes.
- `__tests__/`: Jest tests (`*.test.js`); `__fixtures__/` holds test helpers.
Expand All @@ -13,20 +14,17 @@

## Build, Test, and Development Commands

```bash
npm install # Install dependencies (Node >= 20;
# .node-version pins 24.4.0)
npm run bundle # Format + build action into dist/
npm run package # Build dist/ only (Rollup)
npm run package:watch # Rebuild dist/ on changes
npm run lint # ESLint checks
npm run format:check # Prettier check
npm run format:write # Prettier fix
npm test # Jest test run with coverage
npm run coverage # Update coverage badge
npm run local-action # Run action locally using .env
npm run all # Format, lint, test, coverage, build
```
- `npm install` — Install dependencies (Node >= 20; `.node-version` pins 24.4.0)
- `npm run bundle` — Format + build action into `dist/`
- `npm run package` — Build `dist/` only (Rollup)
- `npm run package:watch` — Rebuild `dist/` on changes
- `npm run lint` — ESLint checks
- `npm run format:check` — Prettier check
- `npm run format:write` — Prettier fix
- `npm test` — Jest test run with coverage
- `npm run coverage` — Update coverage badge
- `npm run local-action` — Run action locally using `.env`
- `npm run all` — Format, lint, test, coverage, build

## Coding Style & Naming Conventions

Expand Down
38 changes: 30 additions & 8 deletions __tests__/post.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ jest.unstable_mockModule('fs', () => mockFs)
// Mock global fetch
global.fetch = jest.fn()

const createFetchResponse = ({ ok = true, status = 200, body = '' } = {}) => ({
ok,
status,
text: () => Promise.resolve(body)
})

// Mock process.kill
const originalKill = process.kill

Expand Down Expand Up @@ -70,10 +76,11 @@ describe('post.js', () => {
return state[key] || ''
})

global.fetch.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ hits: 100, misses: 50 })
})
global.fetch.mockResolvedValue(
createFetchResponse({
body: JSON.stringify({ hits: 100, misses: 50 })
})
)

process.kill = jest.fn()
// Process exits immediately after SIGTERM
Expand Down Expand Up @@ -172,15 +179,30 @@ describe('post.js', () => {
})

it('handles stats with zero total gracefully', async () => {
global.fetch.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ hits: 0, misses: 0 })
})
global.fetch.mockResolvedValue(
createFetchResponse({
body: JSON.stringify({ hits: 0, misses: 0 })
})
)

await run()

expect(core.info).toHaveBeenCalledWith(
expect.stringContaining('Cache hit rate: 0%')
)
})

it('skips non-JSON stats responses without warning', async () => {
global.fetch.mockResolvedValue(
createFetchResponse({
body: 'omni-cache is running'
})
)

await run()

expect(core.warning).not.toHaveBeenCalledWith(
expect.stringContaining('Could not fetch cache statistics')
)
})
})
23 changes: 22 additions & 1 deletion dist/post.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/post.js.map

Large diffs are not rendered by default.

23 changes: 22 additions & 1 deletion src/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,28 @@ async function fetchStats(host) {
try {
const response = await fetch(`${url}/stats`)
if (response.ok) {
const stats = await response.json()
const bodyText =
typeof response.text === 'function' ? await response.text() : ''
const trimmed = (bodyText || '').replace(/^\uFEFF/, '').trim()
if (!trimmed) {
core.debug('omni-cache stats endpoint returned empty response')
return null
}

if (!/^[{[]/.test(trimmed)) {
core.debug(
'omni-cache stats endpoint returned non-JSON response; skipping stats'
)
return null
}

let stats
try {
stats = JSON.parse(trimmed)
} catch (error) {
core.warning(`Could not parse cache statistics JSON: ${error.message}`)
return null
}

core.info('=== omni-cache Statistics ===')
core.info(JSON.stringify(stats, null, 2))
Expand Down
Loading