-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaction.yml
More file actions
175 lines (145 loc) · 5.93 KB
/
action.yml
File metadata and controls
175 lines (145 loc) · 5.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
name: "Codecov"
description: |
Action to upload coverage to Codecov.
- **Automatic dependency management**: Automatically detects and installs missing dependencies (Git, cURL, gnupg) using pkgxdev
- **Configuration fixes**: Automatically fixes pkgxdev configuration issues with unexpanded environment variables in gpgconf.ctl and .curlrc files
- **Cleanup**: Uninstalls dependencies after Codecov upload is complete
- **OIDC support**: Uses OIDC authentication with Codecov for secure uploads
author: hoverkraft
branding:
icon: upload-cloud
color: blue
inputs:
working-directory:
description: |
Working directory where coverage files are located.
Can be absolute or relative to the repository root.
required: false
default: "."
runs:
using: "composite"
steps:
# Check and install dependencies for codecov
- name: Check Codecov dependencies
id: check-codecov-deps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
// Check which dependencies are missing
const deps = {"git": "git", "curl": "curl", "gpg": "gnupg.org"};
const missingDeps = [];
for (const [dep, pkg] of Object.entries(deps)) {
try {
await io.which(dep, true);
} catch {
missingDeps.push(pkg);
}
}
if (missingDeps.length > 0) {
core.setOutput('missing-deps', missingDeps.join(' '));
}
- name: Install missing Codecov dependencies
if: steps.check-codecov-deps.outputs.missing-deps
uses: pkgxdev/setup@f211ee4db3110b42e5a156282372527e7c1ed723 # v4.0.0
with:
+: ${{ steps.check-codecov-deps.outputs.missing-deps }}
- name: Fix unexpanded environment variables
if: steps.check-codecov-deps.outputs.missing-deps
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const templatePattern = /\$\{([^}:]+)(?::-([^}]*))?\}/g;
const unsafeSyntaxPattern = /`|\$\(/;
const resolving = new Set();
const resolvedCache = new Map();
const hasTemplate = (value) => {
if (typeof value !== 'string') {
return false;
}
templatePattern.lastIndex = 0;
return templatePattern.test(value);
};
const getReferencedVariables = (value) => {
templatePattern.lastIndex = 0;
return Array.from(value.matchAll(templatePattern), (match) => match[1]);
};
const sanitizeForDoubleQuotes = (value) => value.replace(/(["\\])/g, '\\$1');
const resolveEnvVariable = async (envKey) => {
const rawValue = process.env[envKey];
if (!hasTemplate(rawValue)) {
core.debug(`Skipping ${envKey} because it does not match the template pattern.`);
return rawValue;
}
if (unsafeSyntaxPattern.test(rawValue)) {
core.debug(`Skipping ${envKey} because it contains unsupported command substitution syntax.`);
return rawValue;
}
if (resolvedCache.has(envKey)) {
return resolvedCache.get(envKey);
}
if (resolving.has(envKey)) {
core.debug(`Detected circular reference while resolving ${envKey}, falling back to defaults.`);
return rawValue;
}
resolving.add(envKey);
const referencedVariables = getReferencedVariables(rawValue);
for (const variableName of referencedVariables) {
if (variableName === envKey) {
continue;
}
await resolveEnvVariable(variableName);
}
const envForBash = { ...process.env };
delete envForBash[envKey];
const scriptLines = [
'set -eo pipefail',
`resolved_value="${sanitizeForDoubleQuotes(rawValue)}"`,
"printf '%s' \"${resolved_value}\"",
];
let resolvedValue = rawValue;
try {
const { stdout } = await exec.getExecOutput(
'bash',
['-c', scriptLines.join('\n')],
{ env: envForBash, silent: true }
);
resolvedValue = stdout.replace(/\r?\n$/, '');
} catch (error) {
core.warning(`Failed to resolve ${envKey}: ${error.message}`);
}
resolving.delete(envKey);
resolvedCache.set(envKey, resolvedValue);
if (resolvedValue !== rawValue) {
core.debug(`Resolved ${envKey}: "${rawValue}" -> "${resolvedValue}"`);
process.env[envKey] = resolvedValue;
core.exportVariable(envKey, resolvedValue);
} else {
core.debug(`No changes for ${envKey}, skipping update.`);
}
return resolvedValue;
};
for (const envKey of Object.keys(process.env)) {
await resolveEnvVariable(envKey);
}
- name: 📊 Upload coverage to Codecov
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
with:
directory: ${{ inputs.working-directory }}
root_dir: ${{ inputs.working-directory }}
working-directory: ${{ inputs.working-directory }}
use_oidc: true
disable_telem: true
fail_ci_if_error: false
# Uninstall pkgxdev dependencies after codecov is done
- name: Uninstall Codecov dependencies
if: always() && steps.check-codecov-deps.outputs.missing-deps
shell: bash
env:
MISSING_DEPS: ${{ steps.check-codecov-deps.outputs.missing-deps }}
run: |
if command -v pkgx &> /dev/null; then
for dep in $MISSING_DEPS; do
echo "Uninstalling $dep..."
pkgx uninstall "$dep" 2>/dev/null || true
done
fi