1
0
Fork 0
mirror of https://github.com/imjasonh/gcp-metrics-action synced 2026-07-18 14:45:47 +00:00

try to fix

Signed-off-by: Jason Hall <jason@chainguard.dev>
This commit is contained in:
Jason Hall 2025-11-05 11:23:42 -05:00
parent 6b22cdfcf8
commit 1aee6938d3
6 changed files with 185 additions and 29 deletions

View file

@ -18,15 +18,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Read service account key
id: sa-key
run: echo "content=$(cat github-actions-metrics-key.json | jq -c)" >> $GITHUB_OUTPUT
- uses: ./
with:
github-token: ${{ github.token }}
gcp-project-id: jason-chainguard
gcp-service-account-key: ${{ steps.sa-key.outputs.content }}
gcp-service-account-key-file: github-actions-metrics-key.json
- uses: actions/setup-node@v4
with:

16
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,16 @@
repos:
- repo: local
hooks:
- id: npm-test
name: Run npm test
entry: npm test
language: system
pass_filenames: false
stages: [commit]
- id: npm-build
name: Run npm build
entry: npm run build
language: system
pass_filenames: false
stages: [commit]

146
README.md
View file

@ -38,7 +38,11 @@ gcloud iam service-accounts keys create github-actions-metrics-key.json \
--iam-account="github-actions-metrics@${PROJECT_ID}.iam.gserviceaccount.com"
```
### 2. Add Service Account Key to GitHub Secrets
### 2. Add Service Account Key to GitHub
You have two options:
**Option A: Use GitHub Secrets (Recommended for production)**
1. Copy the contents of the JSON key file:
```bash
@ -64,9 +68,34 @@ gcloud iam service-accounts keys create github-actions-metrics-key.json \
rm github-actions-metrics-key.json
```
**Option B: Commit the key file (For testing/demo only)**
If you're testing in a personal repository, you can commit the key file directly:
```bash
git add github-actions-metrics-key.json
git commit -m "Add service account key for metrics"
git push
```
**⚠️ Warning:** Only do this in private repositories for testing. For production, always use secrets.
## Usage
### Basic Usage
### Authentication Methods
This action supports two authentication methods:
| Method | Best For | Works With | Setup Complexity |
|--------|----------|------------|------------------|
| **Service Account Key File** | Testing, forks, `pull_request` events | All workflow events | Simple - just create and store a key |
| **Workload Identity Federation** | Production, security-conscious | `push`, `pull_request_target` only | Moderate - requires WIF setup |
**Quick Decision:**
- Using `pull_request` from forks? → **Use Service Account Key File**
- Production workflows on `push`? → **Use Workload Identity Federation**
### Basic Usage (with Service Account Key File)
Add the action as one of the first steps in your workflow. The action will automatically collect metrics in a post-action phase after all other steps complete.
@ -80,16 +109,22 @@ jobs:
runs-on: ubuntu-latest
steps:
# Checkout first
- uses: actions/checkout@v4
# Write secret to file
- name: Setup GCP credentials
run: echo '${{ secrets.GCP_SERVICE_ACCOUNT_KEY }}' > /tmp/gcp-key.json
# Enable metrics collection
- name: Setup OpenTelemetry Metrics
uses: your-org/otel-action@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
gcp-project-id: ${{ secrets.GCP_PROJECT_ID }}
gcp-service-account-key: ${{ secrets.GCP_SERVICE_ACCOUNT_KEY }}
gcp-service-account-key-file: /tmp/gcp-key.json
# Your regular workflow steps
- uses: actions/checkout@v4
- name: Install dependencies
run: npm install
- name: Run tests
@ -100,6 +135,23 @@ jobs:
# Metrics are automatically collected and exported after this job completes
```
### Alternative: Using Committed Key File
If you have the key file committed (for testing):
```yaml
steps:
- uses: actions/checkout@v4
- uses: your-org/otel-action@v1
with:
github-token: ${{ github.token }}
gcp-project-id: your-project-id
gcp-service-account-key-file: path/to/key.json
# Your workflow steps...
```
### Advanced Configuration
```yaml
@ -108,7 +160,7 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
gcp-project-id: ${{ secrets.GCP_PROJECT_ID }}
gcp-service-account-key: ${{ secrets.GCP_SERVICE_ACCOUNT_KEY }}
gcp-service-account-key-file: /tmp/gcp-key.json
# Optional: Customize service name for resource attributes
service-name: 'my-app-ci'
@ -120,7 +172,7 @@ jobs:
metric-prefix: 'ci.metrics'
# Optional: Adjust export interval (milliseconds)
export-interval-millis: '5000'
export-interval-millis: '1000'
```
## Metrics Collected
@ -184,36 +236,74 @@ custom.googleapis.com/github.actions/step.total
| rate(1m)
```
## Alternative: Using Workload Identity Federation
## Alternative: Using Workload Identity Federation (Recommended)
Instead of service account keys, you can use Workload Identity Federation (recommended for production):
Instead of service account keys, you can use Workload Identity Federation (WIF). This is the **recommended** approach for production as it doesn't require managing service account key files.
### Important: Workflow Event Limitations
⚠️ **WIF requires `id-token: write` permission, which is only available for:**
- `push` events
- `pull_request_target` events (runs in the context of the target repo)
❌ **WIF will NOT work with:**
- `pull_request` events (runs in the context of the fork, cannot get OIDC tokens)
For pull requests from forks, you must use service account keys instead.
### WIF Configuration
```yaml
name: CI with WIF
on:
push:
branches: [ main ]
pull_request_target: # Use pull_request_target, not pull_request
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
id-token: write # Required for WIF
actions: read # Required to read workflow/job info
steps:
- name: Setup OpenTelemetry Metrics
uses: your-org/otel-action@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
gcp-project-id: ${{ secrets.GCP_PROJECT_ID }}
# No service account key needed!
- uses: actions/checkout@v4
# Authenticate with GCP first
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
service_account: ${{ secrets.WIF_SERVICE_ACCOUNT }}
# Setup metrics collection (no key file needed)
- uses: your-org/otel-action@v1
with:
github-token: ${{ github.token }}
gcp-project-id: ${{ secrets.GCP_PROJECT_ID }}
# gcp-service-account-key-file is omitted - uses ADC from WIF
# Your workflow steps...
- run: npm install
- run: npm test
```
### Setting Up Workload Identity Federation
1. Create a Workload Identity Pool and Provider in GCP
2. Grant the service account permissions to the pool
3. Add secrets to GitHub:
- `WIF_PROVIDER`: Full resource name of the workload identity provider
- `WIF_SERVICE_ACCOUNT`: Email of the service account
- `GCP_PROJECT_ID`: Your GCP project ID
See [Google's documentation](https://github.com/google-github-actions/auth#workload-identity-federation) for detailed setup instructions.
## Development
### Install Dependencies
@ -240,7 +330,31 @@ This creates:
- `dist/index.js` - Main entry point
- `dist/post/index.js` - Post-action with all dependencies
**Important:** Always run `npm run build` before committing changes. The `dist/` directory must be committed for the action to work in GitHub Actions.
**Important:** The `dist/` directory must be committed for the action to work in GitHub Actions.
### Pre-commit Hooks
This project uses [pre-commit](https://pre-commit.com/) to automatically run tests and build before each commit.
**Setup:**
```bash
# Install pre-commit (if not already installed)
brew install pre-commit # macOS
# or: pip install pre-commit # other platforms
# Install the git hook scripts
pre-commit install
```
Now `npm test` and `npm run build` will run automatically before each commit. If either fails, the commit will be aborted.
**Manual execution:**
```bash
# Run hooks on all files
pre-commit run --all-files
```
### Project Structure

View file

@ -12,8 +12,8 @@ inputs:
gcp-project-id:
description: 'Google Cloud Project ID where metrics will be exported'
required: true
gcp-service-account-key:
description: 'Google Cloud service account key JSON (alternative to ADC/Workload Identity)'
gcp-service-account-key-file:
description: 'Path to Google Cloud service account key JSON file (alternative to ADC/Workload Identity)'
required: false
service-name:
description: 'Service name for OpenTelemetry resource attributes'

21
dist/post/index.js vendored
View file

@ -109,21 +109,36 @@ module.exports = { collectMetrics };
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
const core = __nccwpck_require__(37484);
const fs = __nccwpck_require__(79896);
const path = __nccwpck_require__(16928);
/**
* Parses and validates action configuration from inputs
* @returns {Object} Configuration object
*/
function getConfig() {
const serviceAccountKey = core.getInput('gcp-service-account-key');
const serviceAccountKeyFile = core.getInput('gcp-service-account-key-file');
let serviceAccountKey = null;
// Read service account key from file if provided
if (serviceAccountKeyFile) {
try {
const filePath = path.resolve(serviceAccountKeyFile);
core.info(`Reading service account key from: ${filePath}`);
serviceAccountKey = fs.readFileSync(filePath, 'utf8');
} catch (error) {
core.error(`Failed to read service account key file: ${error.message}`);
throw new Error(`Cannot read service account key file: ${serviceAccountKeyFile}`);
}
}
const config = {
gcpProjectId: core.getInput('gcp-project-id', { required: true }),
gcpServiceAccountKey: serviceAccountKey || null,
gcpServiceAccountKey: serviceAccountKey,
serviceName: core.getInput('service-name') || 'github-actions',
serviceNamespace: core.getInput('service-namespace') || 'ci',
metricPrefix: core.getInput('metric-prefix') || 'github.actions',
exportIntervalMillis: parseInt(core.getInput('export-interval-millis') || '5000', 10),
exportIntervalMillis: parseInt(core.getInput('export-interval-millis') || '1000', 10),
};
// Validate configuration

View file

@ -1,19 +1,34 @@
const core = require('@actions/core');
const fs = require('fs');
const path = require('path');
/**
* Parses and validates action configuration from inputs
* @returns {Object} Configuration object
*/
function getConfig() {
const serviceAccountKey = core.getInput('gcp-service-account-key');
const serviceAccountKeyFile = core.getInput('gcp-service-account-key-file');
let serviceAccountKey = null;
// Read service account key from file if provided
if (serviceAccountKeyFile) {
try {
const filePath = path.resolve(serviceAccountKeyFile);
core.info(`Reading service account key from: ${filePath}`);
serviceAccountKey = fs.readFileSync(filePath, 'utf8');
} catch (error) {
core.error(`Failed to read service account key file: ${error.message}`);
throw new Error(`Cannot read service account key file: ${serviceAccountKeyFile}`);
}
}
const config = {
gcpProjectId: core.getInput('gcp-project-id', { required: true }),
gcpServiceAccountKey: serviceAccountKey || null,
gcpServiceAccountKey: serviceAccountKey,
serviceName: core.getInput('service-name') || 'github-actions',
serviceNamespace: core.getInput('service-namespace') || 'ci',
metricPrefix: core.getInput('metric-prefix') || 'github.actions',
exportIntervalMillis: parseInt(core.getInput('export-interval-millis') || '5000', 10),
exportIntervalMillis: parseInt(core.getInput('export-interval-millis') || '1000', 10),
};
// Validate configuration