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
12 changes: 11 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ on: [pull_request]

jobs:
run:
permissions: read-all
permissions:
id-token: write
contents: read
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
Expand All @@ -24,6 +26,14 @@ jobs:
flags: ${{ matrix.os }}
verbose: true
token: ${{ secrets.CODECOV_ORG_TOKEN }}
- name: Upload test results to Codecov (calculator) (oidc)
uses: ./
with:
use_oidc: true
fail_ci_if_error: true
files: ./demo/calculator/junit.xml
flags: ${{ matrix.os }}
verbose: true
- name: Upload test results to Codecov (demo)
uses: ./
with:
Expand Down
75 changes: 46 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,42 +38,59 @@ steps:
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
```
>**Note**: This assumes that you've set your Codecov token inside *Settings > Secrets* as `CODECOV_TOKEN`. If not, you can [get an upload token](https://docs.codecov.io/docs/frequently-asked-questions#section-where-is-the-repository-upload-token-found-) for your specific repo on [codecov.io](https://www.codecov.io). Keep in mind that secrets are *not* available to forks of repositories.
> [!IMPORTANT]
> This assumes that you've set your Codecov token inside *Settings > Secrets* as `CODECOV_TOKEN`. If not, you can [get an upload token](https://docs.codecov.io/docs/frequently-asked-questions#section-where-is-the-repository-upload-token-found-) for your specific repo on [codecov.io](https://www.codecov.io).
> Keep in mind that secrets are *not* available to forks of repositories.
> View more information on tokenless uploads [here](https://docs.codecov.com/docs/codecov-tokens#uploading-without-a-token).

> [!NOTE]
> If you are using OIDC, you will need to set the `use_oidc` input to `true`, and make sure to add the following to your workflow file:
> ```yaml
> permissions:
> id-token: write
> contents: read
> ...
> steps:
> - uses: codecov/test-results-action@v1
> with:
> use_oidc: true
> ```

## Arguments

Codecov's Action supports inputs from the user. These inputs, along with their descriptions and usage contexts, are listed in the table below:

| Input | Description | Required |
| :--- | :--- | :---: |
| `token` | Repository Codecov token. Used to authorize report uploads | *Required
| `binary` | The file location of a pre-downloaded version of the CLI. If specified, integrity checking will be bypassed. | Optional
| `codecov_yml_path` | Specify the path to the Codecov YML | Optional
| `commit_parent` | Override to specify the parent commit SHA | Optional
| `directory` | Directory to search for test result reports. | Optional
| `disable_search` | Disable search for test result files. This is helpful when specifying what files you want to upload with the --file option. | Optional
| `dry_run` | Don't upload files to Codecov | Optional
| `env_vars` | Environment variables to tag the upload with (e.g. PYTHON \| OS,PYTHON) | Optional
| `exclude` | Folders to exclude from search | Optional
| `fail_ci_if_error` | Specify whether or not CI build should fail if Codecov runs into an error during upload | Optional
| `file` | Path to test result file to upload | Optional
| `files` | Comma-separated list of files to upload | Optional
| `flags` | Flag upload to group test results (e.g. py3.10 | py3.11 | py3.12) | Optional
| `handle_no_reports_found` | Raise no exceptions when no test result reports found | Optional
| `name` | User defined upload name. Visible in Codecov UI | Optional
| `os` | Override the assumed OS. Options are linux \| macos \| windows \| . | Optional
| `override_branch` | Specify the branch name | Optional
| `override_build` | Specify the build number | Optional
| `override_build_url` | The URL of the build where this is running | Optional
| `override_commit` | Specify the commit SHA | Optional
| `override_pr` | Specify the pull request number | Optional
| `report_code` | The code of the report. If unsure, do not include | Optional
| `root_dir` | Used when not in git/hg project to identify project root directory | Optional
| `slug` | Specify the slug manually (Enterprise use) | Optional
| `url` | Specify the base url to upload (Enterprise use) | Optional
| `verbose` | Specify whether the Codecov output should be verbose | Optional
| `version` | Specify which version of the Codecov CLI should be used. Defaults to `latest` | Optional
| `working-directory` | Directory in which to execute codecov.sh | Optional
| `token` | Repository Codecov token. Used to authorize report uploads | *Required
| `binary` | The file location of a pre-downloaded version of the CLI. If specified, integrity checking will be bypassed. | Optional
| `codecov_yml_path` | Specify the path to the Codecov YML | Optional
| `commit_parent` | Override to specify the parent commit SHA | Optional
| `directory` | Directory to search for test result reports. | Optional
| `disable_search` | Disable search for test result files. This is helpful when specifying what files you want to upload with the --file option. | Optional
| `dry_run` | Don't upload files to Codecov | Optional
| `env_vars` | Environment variables to tag the upload with (e.g. PYTHON \| OS,PYTHON) | Optional
| `exclude` | Folders to exclude from search | Optional
| `fail_ci_if_error` | Specify whether or not CI build should fail if Codecov runs into an error during upload | Optional
| `file` | Path to test result file to upload | Optional
| `files` | Comma-separated list of files to upload | Optional
| `flags` | Flag upload to group test results (e.g. py3.10 | py3.11 | py3.12) | Optional
| `handle_no_reports_found` | Raise no exceptions when no test result reports found | Optional
| `name` | User defined upload name. Visible in Codecov UI | Optional
| `os` | Override the assumed OS. Options are linux \| macos \| windows \| . | Optional
| `override_branch` | Specify the branch name | Optional
| `override_build` | Specify the build number | Optional
| `override_build_url` | The URL of the build where this is running | Optional
| `override_commit` | Specify the commit SHA | Optional
| `override_pr` | Specify the pull request number | Optional
| `report_code` | The code of the report. If unsure, do not include | Optional
| `root_dir` | Used when not in git/hg project to identify project root directory | Optional
| `slug` | Specify the slug manually (Enterprise use) | Optional
| `url` | Specify the base url to upload (Enterprise use) | Optional
| `use_oidc` | Use OIDC to authenticate with Codecov | Optional
| `verbose` | Specify whether the Codecov output should be verbose | Optional
| `version` | Specify which version of the Codecov CLI should be used. Defaults to `latest` | Optional
| `working-directory` | Directory in which to execute codecov.sh | Optional

### Example `workflow.yml` with Codecov Action

Expand Down
3 changes: 3 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ inputs:
url:
description: 'Specify the base url to upload (Enterprise use)'
required: false
use_oidc:
description: 'Use OIDC to authenticate with Codecov'
required: false
verbose:
description: 'Specify whether the Codecov output should be verbose'
required: false
Expand Down
48 changes: 38 additions & 10 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32148,6 +32148,16 @@ const getCommand = (filename, generalArgs, command) => {

;// CONCATENATED MODULE: ./src/buildExec.ts
/* eslint-disable @typescript-eslint/no-explicit-any */
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};




Expand Down Expand Up @@ -32324,15 +32334,33 @@ const buildExecutionEnvironment = (token, envVars) => {
}
return uploadOptions;
};
const buildExecutionOptions = (failCi, verbose) => {
const token = core.getInput('token');
const getToken = () => __awaiter(void 0, void 0, void 0, function* () {
let token = core.getInput('token');
let url = core.getInput('url');
const useOIDC = isTrue(core.getInput('use_oidc'));
if (useOIDC) {
if (!url) {
url = 'https://codecov.io';
}
try {
token = yield core.getIDToken(url);
return Promise.resolve(token);
}
catch (err) {
setFailure(`Codecov: Failed to get OIDC token with url: ${url}. ${err.message}`, true);
}
}
return token;
});
const buildExecutionOptions = (failCi, verbose) => __awaiter(void 0, void 0, void 0, function* () {
const token = yield getToken();
const envVars = core.getInput('env_vars');
const cleanedEnvVars = cleanEnvVars(envVars);
const generalArgs = buildGeneralArgs(verbose);
const { uploadExecArgs, uploadCommand } = buildUploadArgs(token, cleanedEnvVars, failCi);
const executionEnvironment = buildExecutionEnvironment(token, cleanedEnvVars);
return { generalArgs, uploadCommand, uploadExecArgs, executionEnvironment };
};
});


;// CONCATENATED MODULE: external "node:child_process"
Expand All @@ -32346,7 +32374,7 @@ const external_node_path_namespaceObject = require("node:path");
// EXTERNAL MODULE: ./node_modules/undici/index.js
var undici = __nccwpck_require__(1773);
;// CONCATENATED MODULE: ./src/validate.ts
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
var validate_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
Expand All @@ -32362,7 +32390,7 @@ var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _argume



const verify = (filename, platform, version, verbose, failCi) => __awaiter(void 0, void 0, void 0, function* () {
const verify = (filename, platform, version, verbose, failCi) => validate_awaiter(void 0, void 0, void 0, function* () {
try {
const uploaderName = getUploaderName(platform);
// Get SHASUM and SHASUM signature files
Expand All @@ -32379,8 +32407,8 @@ const verify = (filename, platform, version, verbose, failCi) => __awaiter(void
console.log(`Received SHA256SUM signature ${shaSig}`);
}
yield external_node_fs_namespaceObject.writeFileSync(external_node_path_namespaceObject.join(__dirname, `${uploaderName}.SHA256SUM.sig`), shaSig);
const validateSha = () => __awaiter(void 0, void 0, void 0, function* () {
const calculateHash = (filename) => __awaiter(void 0, void 0, void 0, function* () {
const validateSha = () => validate_awaiter(void 0, void 0, void 0, function* () {
const calculateHash = (filename) => validate_awaiter(void 0, void 0, void 0, function* () {
const stream = external_node_fs_namespaceObject.createReadStream(filename);
const uploaderSha = external_node_crypto_namespaceObject.createHash(`sha256`);
stream.pipe(uploaderSha);
Expand All @@ -32398,7 +32426,7 @@ const verify = (filename, platform, version, verbose, failCi) => __awaiter(void
`uploader hash: ${hash}, public hash: ${shasum}`, failCi);
}
});
const verifySignature = () => __awaiter(void 0, void 0, void 0, function* () {
const verifySignature = () => validate_awaiter(void 0, void 0, void 0, function* () {
const args = [
'--logger-fd',
'1',
Expand All @@ -32413,7 +32441,7 @@ const verify = (filename, platform, version, verbose, failCi) => __awaiter(void
setFailure(`Codecov: Error verifying gpg signature: ${err.message}`, failCi);
}
});
const importKey = () => __awaiter(void 0, void 0, void 0, function* () {
const importKey = () => validate_awaiter(void 0, void 0, void 0, function* () {
const args = [
'--logger-fd',
'1',
Expand Down Expand Up @@ -32488,7 +32516,7 @@ var src_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _argu

let failCi;
const invokeCLI = (filename, failCi, verbose) => src_awaiter(void 0, void 0, void 0, function* () {
const { generalArgs, uploadCommand, uploadExecArgs, executionEnvironment } = buildExecutionOptions(failCi, verbose);
const { generalArgs, uploadCommand, uploadExecArgs, executionEnvironment } = yield buildExecutionOptions(failCi, verbose);
const doUploadTestResults = () => src_awaiter(void 0, void 0, void 0, function* () {
yield exec.exec(getCommand(filename, generalArgs, uploadCommand).join(' '), uploadExecArgs, executionEnvironment);
});
Expand Down
2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions junit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="jest tests" tests="2" failures="0" errors="0" time="0.985">
<testsuite name="undefined" errors="0" failures="0" skipped="0" timestamp="2025-03-11T16:49:44" time="0.941" tests="2">
<testcase classname=" test uncovered if" name=" test uncovered if" time="0.001">
</testcase>
<testcase classname=" fully covered" name=" fully covered" time="0">
</testcase>
</testsuite>
</testsuites>
4 changes: 2 additions & 2 deletions src/buildExec.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ describe('setting up cli invocation', () => {
expect(uploaderVersion).toEqual('0.1.2');
});

test('execution options', () => {
test('execution options', async () => {
const failCi = true;
const verbose = false;
const {generalArgs, uploadCommand, uploadExecArgs, executionEnvironment} =
buildExecutionOptions(failCi, verbose);
await buildExecutionOptions(failCi, verbose);

expect(uploadCommand).toEqual('do-upload');

Expand Down
28 changes: 26 additions & 2 deletions src/buildExec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import {
getPlatform,
getUploaderName,
} from './helpers';
import {
setFailure,
} from './helpers';


const context = github.context;
Expand Down Expand Up @@ -207,8 +210,29 @@ const buildExecutionEnvironment = (token: string, envVars) => {
return uploadOptions;
};

const buildExecutionOptions = (failCi: boolean, verbose: boolean) => {
const token = core.getInput('token');
const getToken = async (): Promise<string> => {
let token = core.getInput('token');
let url = core.getInput('url');
const useOIDC = isTrue(core.getInput('use_oidc'));
if (useOIDC) {
if (!url) {
url = 'https://codecov.io';
}
try {
token = await core.getIDToken(url);
return Promise.resolve(token);
} catch (err) {
setFailure(
`Codecov: Failed to get OIDC token with url: ${url}. ${err.message}`,
true,
);
}
}
return token;
};

const buildExecutionOptions = async (failCi: boolean, verbose: boolean) => {
const token = await getToken();
const envVars = core.getInput('env_vars');
const cleanedEnvVars = cleanEnvVars(envVars);

Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const invokeCLI = async (
verbose: boolean,
) => {
const {generalArgs, uploadCommand, uploadExecArgs, executionEnvironment} =
buildExecutionOptions(failCi, verbose);
await buildExecutionOptions(failCi, verbose);

const doUploadTestResults = async () => {
await exec.exec(
Expand Down