diff --git a/.github/workflows/coverage-upload.yml b/.github/workflows/coverage-upload.yml index 70f503fc5..cdb19995b 100644 --- a/.github/workflows/coverage-upload.yml +++ b/.github/workflows/coverage-upload.yml @@ -32,7 +32,7 @@ jobs: uses: actions/github-script@v8 with: script: | - /* ================================================================================= */ + /* ================================================================================= */ /* ================================================================================= */ /* =================================== Context ===================================== */ /* ================================================================================= */ @@ -58,28 +58,29 @@ jobs: //Sleep `seconds` seconds (works with floats) const sleep = (seconds) => new Promise(r => setTimeout(r, seconds * 1000)); - //Check if a json object contains something + //Check if a json object contains something or is just {} const is_empty = (json) => Object.keys(json).length === 0; - //List all "Test for " runs for the current event + // List all "Test for " runs for each package in packages. + // If strict is true, only _event is considered + // Otherwise a priority based search strategy is made const get_corresponding_test_runs = async (packages, _event=event, strict=false) => { const test_runs = {}; - - - // Define the filter strategies in order as it's only triggered by changes + + const base_strategies = [ - {branch : branch, head_sha: sha, event : _event }, // Runs that are triggered by any modification on this branch from this event - {branch : branch, head_sha: sha }, // Then we try with the same commit but different event - {branch : branch}, // Then anything else, as if no run was triggered, the code wasn't changed - {} // Otherwise anything else, as if there was no triggered run on the commit + {branch : branch, head_sha: sha, event : _event }, // Runs directly triggered by this event + {branch : branch, head_sha: sha }, // Runs from the same commit + {branch : branch}, // Runs from the same branch + {} // Any run ]; + // strict => we only want runs triggered by this event so, only the first strategy const strategies = strict ? [base_strategies[0]] : base_strategies; for (const package of packages) { let run = null; - - // Try each strategy untila run is found + // Try each strategy until a run is found for (const strategy of strategies) { const { data } = await github.rest.actions.listWorkflowRunsForRepo({ owner : owner, @@ -97,7 +98,7 @@ jobs: return test_runs; }; - // Wait `attempts` times for `seconds` seconds that `run_id` run is over + // Wait `attempts` times for `seconds` seconds for `run_id` run to complete // return null if timed out. const wait_for_run_completion = async (run_id, seconds = 60, attempts=10) => { let i = 0; @@ -116,7 +117,7 @@ jobs: return null; }; - // Return a workflow by its name + // Return a workflow from its name const get_workflow_by_name = async (name) => { const { data: workflows } = await github.rest.actions.listRepoWorkflows({ owner: owner, @@ -126,9 +127,33 @@ jobs: return workflows.workflows.find(w => w.name === name); }; + // Return the reference of an artifact by its name, and an optional associated + // run id. If run id is provided the artifact is fetched from this run, + // otherwise from any workflow, The first one found (most recent) is returned + const get_artifact_from_name = async (name, run_id=undefined) => { + let artifacts; + if (run_id) { + ({ data: artifacts } = await github.rest.actions.listWorkflowRunArtifacts({ + owner: owner, + repo: repo, + run_id: run_id, + name : name + })); + } + else { + ({ data: artifacts } = await github.rest.actions.listArtifactsForRepo({ + owner: owner, + repo: repo, + name : name + })); + } + return artifacts.artifacts?.[0] + }; + // Download and save an artifact as zip file from its name. + // It is saved in the globally defined location `artifact_save_base_path` const save_artifact = async (artifact) => { - const {data : artifact_data } = await github.rest.actions.downloadArtifact({ + const {data : data } = await github.rest.actions.downloadArtifact({ owner : owner, repo : repo, artifact_id : artifact.id, @@ -136,8 +161,8 @@ jobs: }); const filename = path.join(artifact_save_base_path, `${artifact.name}.zip`) - fs.writeFileSync(filename, Buffer.from(artifact_data, "base64")) - } + fs.writeFileSync(filename, Buffer.from(data, "base64")) + }; /* ================================================================================= */ /* ================================================================================= */ /* ================================== Entrypoint =================================== */ @@ -149,7 +174,9 @@ jobs: //Create artifact download folder - if (!fs.existsSync(artifact_save_base_path)) fs.mkdirSync(artifact_save_base_path, { recursive: true }); + if (!fs.existsSync(artifact_save_base_path)){ + fs.mkdirSync(artifact_save_base_path, {recursive: true}); + } //Wait 10 seconds so that we're "sure" that everything else is started await sleep(10); @@ -181,18 +208,12 @@ jobs: // For each package_name, get the corresponding run, // and check if one of its artifact is "merged-coverage-" // If a package doesn't have an artifact, keep track of it - // Otherwise, download it + // Otherwise, download and save it const packages_without_coverage_artifact = []; for (const package of packages) { console.log(`Looking for artifacts in ${package}`) - const { data: artifacts } = await github.rest.actions.listArtifactsForRepo({ - owner: owner, - repo: repo, - name : `merged-coverage-${package.split("_")[1] ?? package}` - }); - - let artifact = artifacts.artifacts?.[0] - + const artifact = await get_artifact_from_name(`merged-coverage-${package.split("_")[1] ?? package}`); + if (artifact) { await save_artifact(artifact) console.log(`Saved artifact ${artifact.name} for ${package}`) @@ -209,7 +230,7 @@ jobs: for (const package of packages_without_coverage_artifact) { console.log(`Triggering test for ${package}...`); - + //Start the workflow const workflow = await get_workflow_by_name(`Test for ${package}`); if (workflow) { await github.rest.actions.createWorkflowDispatch({ @@ -219,21 +240,20 @@ jobs: ref : branch }); - for (let i = 0; i < 30; i++) { - await sleep(10); - const runs = await get_corresponding_test_runs([package], _event='workflow_dispatch', strict=true); - if (runs[package]) { - console.log(`Triggered ${package}, run id = ${runs[package].id}`); - triggered_runs[package] = runs[package]; - break; - } - else{ - console.log(`Failed to detect triggered run for ${package}`) - } + //Try to get the run corresponding to the started workflow + // by polling the API after 30 seconds. + await sleep(10); + ({ [package] : triggered_runs[package]} = await get_corresponding_test_runs([package], _event='workflow_dispatch', strict=true)); + if (triggered_runs[package]) { + console.log(`Triggered ${package}, run id = ${triggered_runs[package].id}`); + } + else{ + console.log(`Failed to detect triggered run for ${package}`) } } } + //Wait for any triggered run if (!is_empty(triggered_runs)){ // Sleep for 10 minutes should be enough for all test runs to be over await sleep(10*60); @@ -244,14 +264,7 @@ jobs: const run = await wait_for_run_completion(triggered_runs[package].id); if (run){ console.log(`Triggered run of ${package} is over.`); - - const { data: artifacts } = await github.rest.actions.listWorkflowRunArtifacts({ - owner: owner, - repo: repo, - run_id: test_runs[package].id - }); - - const artifact = artifacts.artifacts.find(a => a.name === `merged-coverage-${package.split("_")[1] ?? package}`); + const artifact = await get_artifact_from_name(`merged-coverage-${package.split("_")[1] ?? package}`, run.id); if (artifact) { await save_artifact(artifact) @@ -259,7 +272,6 @@ jobs: } else { console.log(`${package} is missing coverage artifact`); - packages_without_coverage_artifact.push(package); } } else { @@ -268,6 +280,7 @@ jobs: } } + - name: Merge coverage files and convert to xml run: | unzip \*.zip @@ -280,4 +293,6 @@ jobs: uses: codecov/codecov-action@v5.5.1 with: token: ${{ secrets.CODECOV_TOKEN }} - files: coverage.xml \ No newline at end of file + files: coverage.xml + + await github.rest.actions.listWorkflowRunArtifacts({owner,repo, run_id : run.id, name : `merged-coverage-${package.split("_")[1] ?? package}`}); \ No newline at end of file