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
56 changes: 53 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,23 @@ Certificates are **grouped by subject**, so an expired cert with a newer valid r
|----------------|----------|---------|---------------------------------------------------------|
| `certificates` | ✅ | — | JSON array of certificate file paths to validate |
| `warning_days` | ❌ | `30` | Days before expiry to emit a warning instead of passing |
| `fail_on_warn` | ❌ | `false` | Fail the job (exit 1) when any certificate is nearing expiry |

---

## Outputs

- Per-certificate validation details printed to the job log
- A Markdown summary table written to the **GitHub Step Summary** panel
| Output | Description |
|--------------|------------------------------------------------------------------------|
| `result` | Overall validation result: `pass`, `warn`, or `fail` |
| `fail_count` | Number of certificates that failed validation with no valid replacement |
| `warn_count` | Number of certificates nearing expiry with no newer replacement |

The action also writes:
- Per-certificate validation details to the job log
- A Markdown summary table to the **GitHub Step Summary** panel
- Exits **1** if any certificate is expired, not yet valid, or cannot be parsed and has no valid replacement
- Exits **0** (with warnings) if all certs are valid but some are nearing expiry
- Exits **0** (with warnings) if all certs are valid but some are nearing expiry (unless `fail_on_warn: true`)

---

Expand Down Expand Up @@ -121,6 +129,48 @@ jobs:
warning_days: '45'
```

### Using outputs for conditional workflows

```yaml
jobs:
validate-certs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: Validate certificates
id: cert-check
uses: AbsaOSS/validate-certificates@v1
with:
certificates: '["./certs/server.crt", "./certs/client.pem"]'
warning_days: '30'

- name: Send Slack alert on warnings or failures
if: steps.cert-check.outputs.result != 'pass'
run: |
echo "Certificate validation result: ${{ steps.cert-check.outputs.result }}"
echo "Failures: ${{ steps.cert-check.outputs.fail_count }}"
echo "Warnings: ${{ steps.cert-check.outputs.warn_count }}"
# Add your Slack webhook or notification logic here

- name: Open ticket on failures only
if: steps.cert-check.outputs.fail_count > 0
run: |
echo "Creating incident ticket for ${{ steps.cert-check.outputs.fail_count }} failed certificate(s)"
# Add your ticketing system integration here
```

### Enforcing strict certificate hygiene

```yaml
- name: Validate certificates with strict policy
uses: AbsaOSS/validate-certificates@v1
with:
certificates: '["./certs/production.crt"]'
warning_days: '60'
fail_on_warn: 'true' # Fail the job if cert expires within 60 days
```

See the [`examples/`](examples/) directory for ready-to-use workflow files.

---
Expand Down
51 changes: 51 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,21 @@ inputs:
description: 'Number of days before expiration to show warning'
required: false
default: '30'
fail_on_warn:
description: 'Whether to fail the action if any certificate(s) is nearing expiry (true/false)'
required: false
default: 'false'

outputs:
result:
description: 'Overall validation result: pass, warn, or fail'
value: ${{ steps.validate.outputs.result }}
fail_count:
description: 'Number of certificates that failed validation with no valid replacement'
value: ${{ steps.validate.outputs.fail_count }}
warn_count:
description: 'Number of certificates nearing expiry with no newer replacement'
value: ${{ steps.validate.outputs.warn_count }}

branding:
icon: 'shield'
Expand All @@ -34,13 +49,21 @@ runs:
using: "composite"
steps:
- name: Validate certificates
id: validate
env:
INPUT_CERTIFICATES: ${{ inputs.certificates }}
INPUT_WARNING_DAYS: ${{ inputs.warning_days }}
INPUT_FAIL_ON_WARN: ${{ inputs.fail_on_warn }}
shell: bash
run: |
set -uo pipefail

FAIL_ON_WARN="${INPUT_FAIL_ON_WARN,,}"
if [[ "$FAIL_ON_WARN" != "true" && "$FAIL_ON_WARN" != "false" ]]; then
echo "::error::INPUT_FAIL_ON_WARN must be 'true' or 'false', got: '$INPUT_FAIL_ON_WARN'"
exit 1
fi

if [[ ! "$INPUT_WARNING_DAYS" =~ ^[0-9]+$ ]]; then
echo "::error::INPUT_WARNING_DAYS must be a non-negative integer, got: '$INPUT_WARNING_DAYS'"
exit 1
Expand Down Expand Up @@ -288,8 +311,36 @@ runs:
else
echo "> ✅ **All certificate(s) are valid (or have valid replacements).**"
fi
echo ""
echo "| Metric | Count |"
echo "|--------|-------|"
echo "| Failed | $FAIL_COUNT |"
echo "| Warnings | $WARN_COUNT |"
} >> "$GITHUB_STEP_SUMMARY"

# -------------------------------------------------------
# Emit outputs for programmatic use
# -------------------------------------------------------
if [[ $FAIL_COUNT -gt 0 || ( "$FAIL_ON_WARN" == "true" && $WARN_COUNT -gt 0 ) ]]; then
RESULT="fail"
elif [[ $WARN_COUNT -gt 0 ]]; then
RESULT="warn"
else
RESULT="pass"
fi

echo "result=$RESULT" >> "$GITHUB_OUTPUT"
echo "fail_count=$FAIL_COUNT" >> "$GITHUB_OUTPUT"
echo "warn_count=$WARN_COUNT" >> "$GITHUB_OUTPUT"

# -------------------------------------------------------
# Enforce fail_on_warn if requested
# -------------------------------------------------------
if [[ "$FAIL_ON_WARN" == "true" && $WARN_COUNT -gt 0 ]]; then
echo "❌ fail_on_warn=true: $WARN_COUNT certificate(s) are nearing expiry. Failing the job."
exit 1
fi
Comment thread
ndivho-makhuvha marked this conversation as resolved.

# -------------------------------------------------------
# Final exit code
# -------------------------------------------------------
Expand Down