File: spread-tests.yaml

package info (click to toggle)
snapd 2.72-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 80,412 kB
  • sloc: sh: 16,506; ansic: 16,211; python: 11,213; makefile: 1,919; exp: 190; awk: 58; xml: 22
file content (536 lines) | stat: -rw-r--r-- 23,105 bytes parent folder | download
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
name: Run spread tests

on:
  workflow_call:
    inputs:
      runs-on:
        description: 'A json list of tags to indicate which runner to use'
        required: true
        type: string
      group:
        description: 'The name of the group of backends, systems, tests, and rules'
        required: true
        type: string
      backend:
        description: 'The spread backend to use (for possible values, check spread.yaml). This cannot be empty'
        required: true
        type: string
      alternative-backend:
        description: 'The spread backend to use when the backend cannot be used'
        required: false
        type: string
      systems:
        description: 'The spread system(s) to use (for possible values, check spread.yaml). If more than one, separate them with a space. To run all, write ALL. To run none, write NONE'
        required: true
        type: string
      tasks:
        description: 'The spread tasks to run. It may be a space-separated list and may contain directories of many tasks or individual ones'
        required: true
        type: string
      rules:
        description: 'The rule .yaml file to use (found under tests/lib/spread/rules) for test discovery'
        required: true
        type: string
      is-fundamental:
        description: 'If true, then will mark results as from fundamental systems when uploading to Grafana'
        required: false
        type: boolean
        default: false
      use-snapd-snap-from-master:
        description: 'If true, will use the snapd snap built on the master branch'
        required: false
        type: boolean
        default: false
      spread-experimental-features:
        description: 'Comma-separated list of experimental snapd features to enable with: snap set system "experimental.<feature-name>=true"'
        required: false
        type: string
      spread-env:
        description: 'Extra environment variables to set before calling spread (format ENV1=VALUE1 ENV2=VALUE2) that are not covered by the other inputs'
        required: false
        type: string
      upload-artifacts:
        description: 'If true, will upload any artifacts that spread produces'
        type: boolean
        required: false
        default: false
      skip-tests:
        description: 'Space-separated list of regex expressions identifying tests to skip. As a regex, they can identify any part of the spread test like an entire suite or a single test. End a single test with $ to avoid inadvertently matching others.'
        required: false
        type: string


jobs:
  run-spread:
    env:
      SPREAD_EXPERIMENTAL_FEATURES: ${{ inputs.spread-experimental-features }}
      GH_TOKEN: ${{ github.token }}

    runs-on: ${{ fromJSON(inputs.runs-on) }}
    steps:

    - name: Verify inputs
      run: |
          if [ -z "${{ inputs.backend }}" ]; then
            echo "You must specify a backend."
            exit 1
          fi
          if [ -z "${{ inputs.systems }}" ]; then
            echo "You must specify a value for systems. If you want to run all, specify ALL. If you want to run none, specify NONE."
            exit 1
          fi

    - name: Cleanup job workspace
      id: cleanup-job-workspace
      run: |
          rm -rf "${{ github.workspace }}"
          mkdir "${{ github.workspace }}"

    - name: Checkout code
      uses: actions/checkout@v4
      with:
        # spread uses tags as delta reference
        fetch-depth: 0

    - name: Set extra spread environment variables
      if: ${{ inputs.spread-env != '' }}
      run: |
          for env in ${{ inputs.spread-env }}; do
            echo "$env" >> $GITHUB_ENV
          done

    - name: Set PR labels as environment variables
      if: ${{ github.event.pull_request.number }}
      run: |
          labels=$(gh pr view ${{ github.event.pull_request.number }} --repo github.com/canonical/snapd --json labels | jq -r '.labels[].name')
          if grep -q '^Skip spread$' <<<$labels; then
            echo "SKIP_SPREAD_LABEL=true" >> $GITHUB_ENV
          fi
          if grep -q '^Run all$' <<<$labels; then
            echo "RUN_ALL_LABEL=true" >> $GITHUB_ENV
          fi
          if grep -q '^Run nested$' <<<$labels; then
            echo "RUN_NESTED_LABEL=true" >> $GITHUB_ENV
          fi

    - name: Get previous attempt
      id: get-previous-attempt
      run: |
        echo "previous_attempt=$(( ${{ github.run_attempt }} - 1 ))" >> $GITHUB_OUTPUT
      shell: bash

    - name: Get previous cache
      uses: actions/cache@v4
      with:
        path: "${{ github.workspace }}/.test-results"
        key: "${{ github.job }}-results-${{ github.run_id }}-${{ inputs.group }}-${{ steps.get-previous-attempt.outputs.previous_attempt }}"

    - name: Prepare test results env and vars
      id: prepare-test-results-env
      run: |
          # Create test results directories and save vars
          TEST_RESULTS_DIR="${{ github.workspace }}/.test-results"
          echo "TEST_RESULTS_DIR=$TEST_RESULTS_DIR" >> $GITHUB_ENV

          # Save the var with the failed tests file
          echo "FAILED_TESTS_FILE=$TEST_RESULTS_DIR/failed-tests" >> $GITHUB_ENV

          # Make sure the test results dirs are created
          # This step has to be after the cache is restored
          mkdir -p "$TEST_RESULTS_DIR"

    - name: Prepare nested env vars
      if: ${{ env.RUN_NESTED_LABEL == 'true' && startsWith(inputs.group, 'nested-') }}
      run: |
          echo "RUN_NESTED=true" >> "$GITHUB_ENV"

    - name: Get changed files
      id: changed-files
      uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46.0.5

    - name: Save changes files
      run: |
          CHANGED_FILES="${{ steps.changed-files.outputs.all_modified_files }}"
          echo "CHANGED_FILES=$CHANGED_FILES" >> $GITHUB_ENV
          echo "The changed files found are: $CHANGED_FILES"

    - name: Check failed tests to run
      if: ${{ env.RUN_ALL_LABEL != 'true' }}
      run: |
          # Save previous failed test results in FAILED_TESTS env var
          FAILED_TESTS=""
          if [ -f "$FAILED_TESTS_FILE" ]; then
              echo "Failed tests file found"
              FAILED_TESTS="$(cat $FAILED_TESTS_FILE)"
              echo "Failed tests to run: $FAILED_TESTS"
              echo "FAILED_TESTS=$FAILED_TESTS" >> $GITHUB_ENV
          fi

    - name: Setup run tests variable
      if: ${{ env.SKIP_SPREAD_LABEL != 'true' }}
      run: |
          get_new_tasks() {
            local prefix=$1
            local changes_param=$2
            # The tests are just filtered when the change is a PR
            # When 'Run Nested' label is added in a PR, all the nested tests have to be executed
            TASKS_TO_RUN=""
            if [ -z "${{ github.event.number }}" ] || [ "$RUN_NESTED" = 'true' ] || [ -z "${{ inputs.rules }}" ] || [ -z "$changes_param" ]; then
                for TASKS in ${{ inputs.tasks }}; do
                    TASKS_TO_RUN="$TASKS_TO_RUN $prefix:$TASKS"
                done
            else
                NEW_TASKS="$(./tests/lib/external/snapd-testing-tools/utils/spread-filter -r ./tests/lib/spread/rules/${{ inputs.rules }}.yaml -p "$prefix" $changes_param -t "${{ inputs.tasks }}")"
                TASKS_TO_RUN="$NEW_TASKS"
            fi
            echo "$TASKS_TO_RUN"
          }

          # Determine if the alternative backend has to be used for the current group
          # Alternative backends usage depends on the value stored in the file USE_ALTERNATIVE_BACKEND,
          # where it is defined the backend to use when images are ready in both google and openstack.
          USE_ALTERNATIVE_BACKEND="$(curl -s https://storage.googleapis.com/snapd-spread-tests/snapd-tests/ci/use_alternative_backend.json)"
          SPREAD_BACKEND="${{ inputs.backend }}"
          if [ -n "${{ inputs.alternative-backend }}" ] && [ $(jq -r ".\"${{ inputs.group }}\"" <<< "$USE_ALTERNATIVE_BACKEND") == true ]; then
              SPREAD_BACKEND="${{ inputs.alternative-backend }}"
          fi
          echo SPREAD_BACKEND="$SPREAD_BACKEND" >> $GITHUB_ENV

          CHANGES_PARAM=""
          for CHANGE in $CHANGED_FILES; do
              CHANGES_PARAM="$CHANGES_PARAM -c $CHANGE"
          done
          RUN_TESTS=""
          # Save previous failed test results in FAILED_TESTS env var
          if [ -n "$FAILED_TESTS" ]; then
              RUN_TESTS="$FAILED_TESTS"
          elif [ "${{ inputs.systems }}" = "ALL" ]; then
              RUN_TESTS=$(get_new_tasks "$SPREAD_BACKEND" "$CHANGES_PARAM")
          elif [ "${{ inputs.systems }}" != "NONE" ]; then
              for SYSTEM in ${{ inputs.systems }}; do
                NEW_TASKS="$(get_new_tasks "$SPREAD_BACKEND:$SYSTEM" "$CHANGES_PARAM")"
                if [ -z "$RUN_TESTS" ]; then
                    RUN_TESTS="$NEW_TASKS"
                else
                    RUN_TESTS="$RUN_TESTS $NEW_TASKS"
                fi
              done
          fi
          echo RUN_TESTS="$RUN_TESTS"  >> $GITHUB_ENV

    - name: Check if system is required
      if: always()
      env:
        GH_TOKEN: ${{ github.token }}
      run: |
        required=$(gh api repos/${{ github.repository }}/rules/branches/master | \
          jq -r '.[] | select(.type=="required_status_checks") | .parameters.required_status_checks' | \
          grep -q "spread ${{ inputs.group }} /" && echo "req" || echo "not-req")
        echo REQUIRED="$required" >> $GITHUB_ENV

    - name: Setup grafana parameters
      if: ${{ env.SKIP_SPREAD_LABEL != 'true' }}
      run: |
          # Configure parameters to filter logs (these logs are sent read by grafana agent)
          CHANGE_ID="${{ github.event.number }}"
          if [ -z "$CHANGE_ID" ]; then
            CHANGE_ID="main"
          fi
          fundamental=""
          if [ "${{ inputs.is-fundamental }}" = 'true' ]; then
            fundamental="fund"
          fi
          FILTERED_LOG_FILE="spread_${CHANGE_ID}_${fundamental}_${REQUIRED}_n${{ github.run_attempt }}.filtered.log"
          # The log-filter tool is used to filter the spread logs to be stored
          echo FILTER_PARAMS="-o $FILTERED_LOG_FILE -e Debug -e WARNING: -f Failed=NO_LINES -f Error=NO_LINES"  >> $GITHUB_ENV
          echo FILTERED_LOG_FILE="$FILTERED_LOG_FILE"  >> $GITHUB_ENV

          # Add start line to filtered log
          echo "GRAFANA START: pr ${CHANGE_ID} attempt ${{ github.run_attempt }} run ${{ github.run_id }} group ${{ inputs.group }}" > "$FILTERED_LOG_FILE"

    - name: Download and extract pre-built packages if they exist
      if: ${{ env.SKIP_SPREAD_LABEL != 'true' }}
      env:
        SYSTEMS: ${{ inputs.systems }}
      uses: actions/github-script@v7
      with:
        script: |
          let fs = require('fs');
          let path = require('path');
          let os = require('os');
          let child_process = require('child_process');
          let systems = process.env.SYSTEMS.split(' ');

          let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
            owner: context.repo.owner,
            repo: context.repo.repo,
            run_id: context.runId,
          });
          
          for (let system of systems) {
            let artifactName = `package-${system}`;
            const match = system.match(/^ubuntu-core-(\d+)(-arm)?-64$/);
            if (match) {
              const version = match[1];
              const arch = match[2] || '';
              artifactName = `package-ubuntu-${version}.04${arch}-64`;
            }
            let artifact = allArtifacts.data.artifacts.find(a => a.name === artifactName);
            if (!artifact) {
              console.log(`Artifact not found for system: ${system}`);
              continue;
            }
            console.log(`Found artifact "${artifactName}" with id ${artifact.id}, downloading...`);

            const download = await github.rest.actions.downloadArtifact({
              owner: context.repo.owner,
              repo: context.repo.repo,
              artifact_id: artifact.id,
              archive_format: 'zip',
            });

            const artifactZipPath = path.join(process.env.GITHUB_WORKSPACE, `${artifactName}.zip`);
            fs.writeFileSync(artifactZipPath, Buffer.from(download.data));

            const extractDir = path.join(process.env.GITHUB_WORKSPACE, 'built-pkgs', system);
            fs.mkdirSync(extractDir, { recursive: true });
            child_process.execSync(`unzip -q -o ${artifactZipPath} -d ${extractDir}`);

            fs.unlinkSync(artifactZipPath);
          }

    - name: Set whether to use pre-built packages in spread
      run: |
        SPREAD_USE_PREBUILT_PACKAGES=true
        for system in ${{ inputs.systems }}; do
          if ! [ -d built-pkgs/"$system" ]; then
            SPREAD_USE_PREBUILT_PACKAGES=false
            echo "Disabling prebuilt packages due to missing $system directory"
          fi
        done
        echo "SPREAD_USE_PREBUILT_PACKAGES=$SPREAD_USE_PREBUILT_PACKAGES" >> $GITHUB_ENV

    - name: Download built snap (amd64)
      uses: actions/download-artifact@v4
      if: "${{ env.SKIP_SPREAD_LABEL != 'true' && !inputs.use-snapd-snap-from-master && !contains(inputs.group, '-arm64') && !contains(inputs.group, '-fips') }}"
      with:
        name: snap-files-amd64-default-test
        # eg. snapd_1337.2.65.1+g97.d35b459_amd64.snap
        pattern: snapd_1337.*.snap
        path: "${{ github.workspace }}/built-snap"

    - name: Download built snap (arm64)
      if: "${{ env.SKIP_SPREAD_LABEL != 'true' && !inputs.use-snapd-snap-from-master && contains(inputs.group, '-arm64') && !contains(inputs.group, '-fips') }}"
      uses: actions/download-artifact@v4
      with:
        name: snap-files-arm64-default-test
        pattern: snapd_1337.*.snap
        # eg. snapd_1337.2.65.1+g97.d35b459_amd64.snap
        path: "${{ github.workspace }}/built-snap"

    - name: Download built FIPS snap (amd64)
      uses: actions/download-artifact@v4
      # eg. ubuntu-fips-deb, ubuntu-fips-snap
      if: "${{ env.SKIP_SPREAD_LABEL != 'true' && !inputs.use-snapd-snap-from-master && !contains(inputs.group, '-arm64') && contains(inputs.group, '-fips-') }}"
      with:
        name: snap-files-amd64-FIPS-test
        # eg. snapd_1337.2.65.1+g97.d35b459-fips_amd64.snap
        pattern: snapd_1337.*-fips_*.snap
        path: "${{ github.workspace }}/built-snap"

    - name: Rename imported snap
      if: "${{ env.SKIP_SPREAD_LABEL != 'true' && !inputs.use-snapd-snap-from-master }}"
      run: |
        for snap in built-snap/snapd_1337.*.snap; do
          mv -v "${snap}" "${snap}.keep"
        done

    - name: Run spread tests
      if: ${{ env.SKIP_SPREAD_LABEL != 'true' }}
      env:
          SPREAD_GOOGLE_KEY: ${{ secrets.SPREAD_GOOGLE_KEY }}
      run: |
          # Register a problem matcher to highlight spread failures
          echo "::add-matcher::.github/spread-problem-matcher.json"

          SPREAD=spread
          if [[ "${{ inputs.group }}" =~ nested- ]]; then
            export NESTED_BUILD_SNAPD_FROM_CURRENT=true
            export NESTED_ENABLE_KVM=true
          fi

          export SPREAD_USE_PREBUILT_SNAPD_SNAP=true
          
          if [ "${{ inputs.use-snapd-snap-from-master }}" = true ]; then
            if [[ "${{ inputs.group }}" == *"-arm64"* ]]; then
              export SPREAD_USE_SNAPD_SNAP_URL=https://storage.googleapis.com/snapd-spread-tests/snapd-tests/snaps/snapd_master_arm64.snap
            else
              export SPREAD_USE_SNAPD_SNAP_URL=https://storage.googleapis.com/snapd-spread-tests/snapd-tests/snaps/snapd_master_amd64.snap
            fi
          fi

          # This could be the case when either there are not systems for a group or
          # the list of tests to run is empty
          if [ -z "$RUN_TESTS" ]; then
            echo "No tests to run, exiting..."
            exit 0
          fi

          spread_list="$($SPREAD -list $RUN_TESTS 2>&1 || true)"
          if [ -n "${{ inputs.skip-tests }}" ]; then
            for test in ${{ inputs.skip-tests }}; do
              spread_list=$(echo "$spread_list" | tr ' ' '\n' | grep -vE "$test" | tr '\n' ' ')
            done
            export RUN_TESTS=$spread_list
            echo RUN_TESTS="$RUN_TESTS"  >> $GITHUB_ENV
          fi

          # If all spread tests are skipped, exit
          if [ -z "${RUN_TESTS// /}" ]; then
            echo "All tests are skipped, exiting..."
            exit 0
          fi

          if [[ "$spread_list" =~ amazon-linux-2023 ]]; then
              # Amazon Linux 2023 has no xdelta, however we cannot disable
              # xdelta on a per-target basis as it's used in the repack section
              # of spread.yaml, which is shared by all targets, so all systems
              # in this batch will not use delta for transferring project data
              echo "Disabling xdelta support"
              export NO_DELTA=1
          fi

          if grep -q "nothing matches provider filter" <<< "$spread_list"; then
            echo "No tests to run, exiting..."
            exit 0
          fi

          SPREAD_FLAGS='-no-debug-output -logs spread-logs'
          if [ "${{ inputs.upload-artifacts }}" = true ]; then
            SPREAD_FLAGS="$SPREAD_FLAGS -artifacts spread-artifacts"
            echo "ARTIFACTS_FOLDER=spread-artifacts" >> $GITHUB_ENV
          fi

          # Run spread tests
          # "pipefail" ensures that a non-zero status from the spread is
          # propagated; and we use a subshell as this option could trigger
          # undesired changes elsewhere
          echo "Running command: $SPREAD $RUN_TESTS"
          (
            set -o pipefail 
            $SPREAD $SPREAD_FLAGS $RUN_TESTS | \
              ./tests/lib/external/snapd-testing-tools/utils/log-filter $FILTER_PARAMS | \
              tee spread.log
          )

    - name: Discard spread workers
      if: always()
      run: |
        shopt -s nullglob;
        for r in .spread-reuse.*.yaml; do
          spread -discard -reuse-pid="$(echo "$r" | grep -o -E '[0-9]+')";
        done

    - name: Upload spread logs
      if: always()
      uses: actions/upload-artifact@v4
      with:
        name: "spread-logs-${{ inputs.group }}-${{ inputs.systems }}"
        path: "spread-logs/*.log"
        if-no-files-found: ignore

    - name: Report spread errors
      if: always()
      run: |
        if [ -e spread.log ]; then
            echo "Running spread log analyzer"
            ACTIONS_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/attempts/${{ github.run_attempt }}"
            ./tests/lib/external/snapd-testing-tools/utils/log-parser spread.log --output spread-results.json --cut 1 >/dev/null
            while IFS= read -r line; do
                if [ ! -z "$line" ]; then
                    echo "Adding failed test line to filtered log"
                    echo "GRAFANA FAILED: $line $ACTIONS_URL" | tee -a "$FILTERED_LOG_FILE"
                fi
            done <<< $(jq -r '.[] | select( .type == "info" ) | select( .info_type == "Error" ) | "\(.verb) \(.task)"' spread-results.json)
        else
            echo "No spread log found, skipping errors reporting"
        fi

    - name: Analyze spread test results
      if: always()
      run: |
          if [ -f spread.log ]; then
              echo "Running spread log parser"
              ./tests/lib/external/snapd-testing-tools/utils/log-parser spread.log --output spread-results.json

              # Add openstack backend definition to spread.yaml
              if [ "$SPREAD_BACKEND" = openstack ]; then
                  ./tests/lib/spread/add-backend tests/lib/spread/backend.openstack.yaml spread.yaml
              fi

              echo "Running spread log analyzer"
              ./tests/lib/external/snapd-testing-tools/utils/log-analyzer list-reexecute-tasks "$RUN_TESTS" spread-results.json > "$FAILED_TESTS_FILE"

              if [ -n "${{ inputs.upload-artifacts }}" ] && \
                  [ -d "$ARTIFACTS_FOLDER" ] && \
                  grep -Eq "Failed (task|suite|project) restore: " spread.log; then
                  
                  # Restore encountered some errors, so check the retrieved data for any missing tests.
                  # If any data is missing, add the corresponding test to the list of failed tests.
                  for test in $RUN_TESTS; do
                      dir="$(find $ARTIFACTS_FOLDER -name "$(sed 's/\//--/g' <<<$test)" -type d 2>/dev/null || true)"
                      if [ -z "$dir" ] && ! grep -q "$test" "$FAILED_TESTS_FILE"; then
                          printf "%s %s\n" "$(< $FAILED_TESTS_FILE)" "$test" > "$FAILED_TESTS_FILE"
                      fi
                  done
              fi

              echo "List of failed tests saved"
              cat "$FAILED_TESTS_FILE"
          else
              echo "No spread log found, saving empty list of failed tests"
              touch "$FAILED_TESTS_FILE"
          fi
          
          # Set the TEST_FAILED variable if there are any failed tests
          if [ -n "$(cat $FAILED_TESTS_FILE)" ]; then
            echo "TEST_FAILED=true" >> $GITHUB_ENV
          fi

    - name: Prepare artifact upload
      if: always() && fromJSON(inputs.upload-artifacts)
      run: |
        if [ -z "${ARTIFACTS_FOLDER}" ]; then
            echo "No artifacts folder found"
            exit 0
        fi

        echo "${{ github.run_attempt }}" > "${ARTIFACTS_FOLDER}/run-attempt.txt"
        cp "$FAILED_TESTS_FILE" "${ARTIFACTS_FOLDER}/failed-tests.txt"
        echo "SPREAD_EXPERIMENTAL_FEATURES=${SPREAD_EXPERIMENTAL_FEATURES}" > "${ARTIFACTS_FOLDER}/env-variables.txt"
        echo "SPREAD_SNAPD_DEB_FROM_REPO=${SPREAD_SNAPD_DEB_FROM_REPO}" >> "${ARTIFACTS_FOLDER}/env-variables.txt"
        tar -czf "spread-artifacts-${{ inputs.group }}-${{ inputs.systems }}_${{ github.run_id }}_${{ github.run_attempt }}.tar.gz" "$ARTIFACTS_FOLDER"

    - name: Upload artifacts
      if: always() && fromJSON(inputs.upload-artifacts)
      uses: actions/upload-artifact@v4
      with:
        name: "spread-artifacts-${{ inputs.group }}-${{ inputs.systems }}_${{ github.run_id }}_${{ github.run_attempt }}"
        path: "spread-artifacts-${{ inputs.group }}-${{ inputs.systems }}_${{ github.run_id }}_${{ github.run_attempt }}.tar.gz"
        if-no-files-found: ignore

    - name: Save spread test results to cache
      if: always()
      uses: actions/cache/save@v4
      with:
        path: "${{ github.workspace }}/.test-results"
        key: "${{ github.job }}-results-${{ github.run_id }}-${{ inputs.group }}-${{ github.run_attempt }}"

    - name: Save spread.json as an artifact
      if: ${{ failure() && github.event.pull_request.number && env.TEST_FAILED == 'true' }}
      uses: actions/upload-artifact@v4
      with:
        name: "spread-json-${{ github.run_id }}-${{ github.run_attempt }}-${{ inputs.group }}" 
        path: "spread-results.json"
        if-no-files-found: ignore