Skip to content

Commit 2d0587f

Browse files
Copilotneilime
andcommitted
refactor: move Codecov logic to dedicated action with dependency cleanup
Co-authored-by: neilime <[email protected]>
1 parent d4a9643 commit 2d0587f

File tree

4 files changed

+169
-89
lines changed

4 files changed

+169
-89
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ _Actions for continuous integration steps: build, lint, and test._
2424

2525
#### - [Build](actions/build/README.md)
2626

27+
#### - [Codecov](actions/codecov/README.md)
28+
2729
#### - [Lint](actions/lint/README.md)
2830

2931
#### - [Test](actions/test/README.md)

actions/codecov/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Codecov Action
2+
3+
Action to upload coverage to Codecov with support for container mode.
4+
5+
## Overview
6+
7+
This action handles uploading code coverage to Codecov. When running in container mode, it automatically installs required dependencies (Git, cURL, gnupg) via pkgxdev, fixes configuration issues with unexpanded environment variables, uploads coverage, and then cleans up the installed dependencies.
8+
9+
## Usage
10+
11+
```yaml
12+
- uses: hoverkraft-tech/ci-github-nodejs/actions/codecov@main
13+
with:
14+
# Working directory where coverage files are located.
15+
# Can be absolute or relative to the repository root.
16+
#
17+
# Default: `.`
18+
working-directory: .
19+
20+
# Whether running in container mode (installs dependencies if needed)
21+
# Default: `false`
22+
container: "false"
23+
```
24+
25+
## Inputs
26+
27+
| **Input** | **Description** | **Required** | **Default** |
28+
| ----------------------- | ------------------------------------------------------------------- | ------------ | ----------- |
29+
| **`working-directory`** | Working directory where coverage files are located. | **false** | `.` |
30+
| **`container`** | Whether running in container mode (installs dependencies if needed) | **false** | `false` |
31+
32+
## Features
33+
34+
- **Automatic dependency management**: In container mode, automatically detects and installs missing dependencies (Git, cURL, gnupg) using pkgxdev
35+
- **Configuration fixes**: Automatically fixes pkgxdev configuration issues with unexpanded environment variables in gpgconf.ctl and .curlrc files
36+
- **Cleanup**: Uninstalls dependencies after Codecov upload is complete
37+
- **OIDC support**: Uses OIDC authentication with Codecov for secure uploads

actions/codecov/action.yml

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
name: "Codecov"
2+
description: "Action to upload coverage to Codecov with support for container mode"
3+
author: hoverkraft
4+
branding:
5+
icon: upload-cloud
6+
color: blue
7+
8+
inputs:
9+
working-directory:
10+
description: |
11+
Working directory where coverage files are located.
12+
Can be absolute or relative to the repository root.
13+
required: false
14+
default: "."
15+
container:
16+
description: "Whether running in container mode (installs dependencies if needed)"
17+
required: false
18+
default: "false"
19+
20+
runs:
21+
using: "composite"
22+
steps:
23+
# Check and install dependencies for codecov in container mode
24+
- name: Check Codecov dependencies
25+
if: inputs.container == 'true'
26+
id: check-codecov-deps
27+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
28+
with:
29+
script: |
30+
// Check which dependencies are missing
31+
const deps = {"git": "git", "curl": "curl", "gpg": "gnupg.org"};
32+
const missingDeps = [];
33+
34+
for (const [dep, pkg] of Object.entries(deps)) {
35+
try {
36+
await io.which(dep, true);
37+
} catch {
38+
missingDeps.push(pkg);
39+
}
40+
}
41+
42+
if (missingDeps.length > 0) {
43+
core.setOutput('missing-deps', missingDeps.join(' '));
44+
}
45+
46+
- name: Install missing Codecov dependencies
47+
if: steps.check-codecov-deps.outputs.missing-deps
48+
uses: pkgxdev/setup@f211ee4db3110b42e5a156282372527e7c1ed723 # v4.0.0
49+
with:
50+
+: ${{ steps.check-codecov-deps.outputs.missing-deps }}
51+
52+
# Fix pkgxdev gnupg's gpgconf.ctl which contains unexpanded environment variables
53+
- name: Fix GPG configuration
54+
if: contains(steps.check-codecov-deps.outputs.missing-deps, 'gnupg.org')
55+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
56+
with:
57+
script: |
58+
const fs = require('node:fs');
59+
const path = require('node:path');
60+
const os = require('node:os');
61+
62+
// Find and remove the malformed gpgconf.ctl file that contains unexpanded shell variables
63+
// The pkgxdev gnupg installs gpgconf.ctl next to the gpgconf binary
64+
try {
65+
const gpgconfPath = await io.which('gpgconf', false);
66+
if (gpgconfPath) {
67+
const gpgconfCtl = path.join(path.dirname(gpgconfPath), 'gpgconf.ctl');
68+
if (fs.existsSync(gpgconfCtl)) {
69+
core.info(`Removing malformed gpgconf.ctl: ${gpgconfCtl}`);
70+
await io.rmRF(gpgconfCtl);
71+
}
72+
}
73+
} catch (error) {
74+
core.warning(`Failed to check/remove gpgconf.ctl: ${error.message}`);
75+
}
76+
77+
// Ensure GNUPGHOME is set up correctly
78+
const gnupgHome = path.join(os.homedir(), '.gnupg');
79+
await io.mkdirP(gnupgHome);
80+
fs.chmodSync(gnupgHome, 0o700);
81+
82+
# Fix pkgxdev curl's .curlrc which contains unexpanded environment variables
83+
- name: Fix curl configuration
84+
if: contains(steps.check-codecov-deps.outputs.missing-deps, 'curl')
85+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
86+
with:
87+
script: |
88+
const fs = require('node:fs');
89+
const path = require('node:path');
90+
91+
// Find and fix the .curlrc file that contains unexpanded shell variables like ${SSL_CERT_FILE:-...}
92+
// The pkgxdev curl installs .curlrc next to the curl binary
93+
try {
94+
const curlPath = await io.which('curl', false);
95+
if (curlPath) {
96+
const curlrc = path.join(path.dirname(curlPath), '.curlrc');
97+
if (fs.existsSync(curlrc)) {
98+
core.info(`Removing malformed .curlrc: ${curlrc}`);
99+
await io.rmRF(curlrc);
100+
}
101+
}
102+
} catch (error) {
103+
core.warning(`Failed to check/remove .curlrc: ${error.message}`);
104+
}
105+
106+
- name: 📊 Upload coverage to Codecov
107+
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
108+
with:
109+
directory: ${{ inputs.working-directory }}
110+
root_dir: ${{ inputs.working-directory }}
111+
working-directory: ${{ inputs.working-directory }}
112+
use_oidc: true
113+
disable_telem: true
114+
fail_ci_if_error: false
115+
116+
# Uninstall pkgxdev dependencies after codecov is done
117+
- name: Uninstall Codecov dependencies
118+
if: always() && steps.check-codecov-deps.outputs.missing-deps
119+
shell: bash
120+
env:
121+
MISSING_DEPS: ${{ steps.check-codecov-deps.outputs.missing-deps }}
122+
run: |
123+
if command -v pkgx &> /dev/null; then
124+
for dep in $MISSING_DEPS; do
125+
echo "Uninstalling $dep..."
126+
pkgx uninstall "$dep" 2>/dev/null || true
127+
done
128+
fi

actions/test/action.yml

Lines changed: 2 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -142,99 +142,12 @@ runs:
142142
title: "Code Coverage Report"
143143
body: ${{ steps.parse-coverage-reports.outputs.markdown }}
144144

145-
# Check and install dependencies for codecov in container mode
146-
- name: Check and install Codecov dependencies
147-
if: always() && inputs.coverage == 'codecov' && inputs.container == 'true'
148-
id: check-codecov-deps
149-
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
150-
with:
151-
script: |
152-
// Check which dependencies are missing
153-
const deps = {"git": "git", "curl": "curl", "gpg": "gnupg.org"};
154-
const missingDeps = [];
155-
156-
for (const [dep, pkg] of Object.entries(deps)) {
157-
try {
158-
await io.which(dep, true);
159-
} catch {
160-
missingDeps.push(pkg);
161-
}
162-
}
163-
164-
if (missingDeps.length > 0) {
165-
core.setOutput('missing-deps', missingDeps.join(' '));
166-
}
167-
168-
- name: Install missing Codecov dependencies
169-
if: always() && steps.check-codecov-deps.outputs.missing-deps
170-
uses: pkgxdev/setup@f211ee4db3110b42e5a156282372527e7c1ed723 # v4.0.0
171-
with:
172-
+: ${{ steps.check-codecov-deps.outputs.missing-deps }}
173-
174-
# Fix pkgxdev gnupg's gpgconf.ctl which contains unexpanded environment variables
175-
- name: Fix GPG configuration
176-
if: always() && contains(steps.check-codecov-deps.outputs.missing-deps, 'gnupg.org')
177-
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
178-
with:
179-
script: |
180-
const fs = require('node:fs');
181-
const path = require('node:path');
182-
const os = require('node:os');
183-
184-
// Find and remove the malformed gpgconf.ctl file that contains unexpanded shell variables
185-
// The pkgxdev gnupg installs gpgconf.ctl next to the gpgconf binary
186-
try {
187-
const gpgconfPath = await io.which('gpgconf', false);
188-
if (gpgconfPath) {
189-
const gpgconfCtl = path.join(path.dirname(gpgconfPath), 'gpgconf.ctl');
190-
if (fs.existsSync(gpgconfCtl)) {
191-
core.info(`Removing malformed gpgconf.ctl: ${gpgconfCtl}`);
192-
await io.rmRF(gpgconfCtl);
193-
}
194-
}
195-
} catch (error) {
196-
core.warning(`Failed to check/remove gpgconf.ctl: ${error.message}`);
197-
}
198-
199-
// Ensure GNUPGHOME is set up correctly
200-
const gnupgHome = path.join(os.homedir(), '.gnupg');
201-
await io.mkdirP(gnupgHome);
202-
fs.chmodSync(gnupgHome, 0o700);
203-
204-
# Fix pkgxdev curl's .curlrc which contains unexpanded environment variables
205-
- name: Fix curl configuration
206-
if: always() && contains(steps.check-codecov-deps.outputs.missing-deps, 'curl')
207-
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
208-
with:
209-
script: |
210-
const fs = require('node:fs');
211-
const path = require('node:path');
212-
213-
// Find and fix the .curlrc file that contains unexpanded shell variables like ${SSL_CERT_FILE:-...}
214-
// The pkgxdev curl installs .curlrc next to the curl binary
215-
try {
216-
const curlPath = await io.which('curl', false);
217-
if (curlPath) {
218-
const curlrc = path.join(path.dirname(curlPath), '.curlrc');
219-
if (fs.existsSync(curlrc)) {
220-
core.info(`Removing malformed .curlrc: ${curlrc}`);
221-
await io.rmRF(curlrc);
222-
}
223-
}
224-
} catch (error) {
225-
core.warning(`Failed to check/remove .curlrc: ${error.message}`);
226-
}
227-
228145
- name: 📊 Upload coverage to Codecov
229146
if: always() && inputs.coverage == 'codecov'
230-
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
147+
uses: ./self-test-action/codecov
231148
with:
232-
directory: ${{ inputs.working-directory }}
233-
root_dir: ${{ inputs.working-directory }}
234149
working-directory: ${{ inputs.working-directory }}
235-
use_oidc: true
236-
disable_telem: true
237-
fail_ci_if_error: false
150+
container: ${{ inputs.container }}
238151

239152
# FIXME: workaround until will be merged: https://github.com/actions/runner/pull/1684
240153
- shell: bash

0 commit comments

Comments
 (0)