Skip to content

Commit a39709d

Browse files
docs: Add guide for multiple operating systems (#3257)
Tested in https://github.com/mihaimaruseac/slsa-test-os/ --------- Signed-off-by: Mihai Maruseac <[email protected]> Co-authored-by: laurentsimon <[email protected]>
1 parent 0f54290 commit a39709d

File tree

1 file changed

+170
-0
lines changed

1 file changed

+170
-0
lines changed

internal/builders/generic/README.md

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ project simply generates provenance as a separate step in an existing workflow.
4242
- [Provenance for matrix strategy builds](#provenance-for-matrix-strategy-builds)
4343
- [A single provenance attestation for all artifacts](#a-single-provenance-attestation-for-all-artifacts)
4444
- [A different attestation for each iteration](#a-different-attestation-for-each-iteration)
45+
- [Provenance for artifacts built across multiple operating systems](#provenance-for-artifacts-built-across-multiple-operating-systems)
4546
- [Known Issues](#known-issues)
4647
- [Skip output 'hashes' since it may contain secret](#skip-output-hashes-since-it-may-contain-secret)
4748
- ['internal error' when using `upload-assets`](#internal-error-when-using-upload-assets)
@@ -1408,6 +1409,175 @@ jobs:
14081409
upload-assets: true # Optional: Upload to a new release
14091410
```
14101411

1412+
## Provenance for artifacts built across multiple operating systems
1413+
1414+
If a single release job produces artifacts for multiple operating systems (using
1415+
matrix strategy), there are a few more caveats to consider:
1416+
1417+
1. First, it is ideal to make Windows behave the same as Linux and MacOS by
1418+
making the runner use `bash` as the shell to execute commands in:
1419+
1420+
```yaml
1421+
defaults:
1422+
run:
1423+
shell: bash
1424+
```
1425+
1426+
2. Optionally, you might also want to make the workflow use LF as line
1427+
terminator even on Windows:
1428+
1429+
```yaml
1430+
- run: git config --global core.autocrlf input
1431+
# Alternatively, also force line endings for every file
1432+
# - run: |
1433+
# git config --global core.eol lf
1434+
# git config --global core.autocrlf input
1435+
```
1436+
1437+
The other complexity arises from the fact that the utilities used to compute the
1438+
digest (`sha256sum`) and the base 64 encoding (`base64`) have different
1439+
behaviors across the operating systems:
1440+
1441+
- On MacOS, the utlity to compute the digest is called `shasum` and the
1442+
algorithm is passed via the `-a 256` algorithm
1443+
- On Windows, we need to tell `sha256sum` to treat all files as text by using
1444+
the `-t` switch, otherwise the output will contain an extra `*` in front of
1445+
the filename. This will later be wrongly interpretted as a glob pattern, so we
1446+
should avoid it.
1447+
- On MacOS, `base64` does not have a `-w0` option, the line wrapping is
1448+
implicit.
1449+
1450+
One way to merge all these differences is to use the bash `||` operator:
1451+
1452+
```yaml
1453+
- id: hash
1454+
run: |
1455+
set -euo pipefail
1456+
(sha256sum -t release_artifact_${{ runner.os }} || shasum -a 256 release_artifact_${{ runner.os }}) > checksum
1457+
echo "hash-${{ matrix.os }}=$(base64 -w0 checksum || base64 checksum)" >> "${GITHUB_OUTPUT}"
1458+
```
1459+
1460+
Thus, to generate a single provenance for artifacts built on all 3 operating
1461+
systems, you would use the following example:
1462+
1463+
```yaml
1464+
defaults:
1465+
run:
1466+
shell: bash
1467+
1468+
jobs:
1469+
build:
1470+
runs-on: ${{ matrix.os }}
1471+
strategy:
1472+
fail-fast: false # Don't cancel other jobs if one fails
1473+
matrix:
1474+
os: [ubuntu-latest, macos-latest, windows-latest]
1475+
outputs:
1476+
hash-ubuntu-latest: ${{ steps.hash.outputs.hash-ubuntu-latest }}
1477+
hash-macos-latest: ${{ steps.hash.outputs.hash-macos-latest }}
1478+
hash-windows-latest: ${{ steps.hash.outputs.hash-windows-latest }}
1479+
steps:
1480+
- run: git config --global core.autocrlf input
1481+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
1482+
with:
1483+
persist-credentials: false
1484+
1485+
# Do the build to create release_artifact_${{ runner.os }}
1486+
- run: ...
1487+
1488+
- uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
1489+
with:
1490+
path: release_artifact_${{ runner.os }}
1491+
name: release_artifact_${{ runner.os }}
1492+
if-no-files-found: error
1493+
- id: hash
1494+
run: |
1495+
set -euo pipefail
1496+
(sha256sum -t release_artifact_${{ runner.os }} || shasum -a 256 release_artifact_${{ runner.os }}) > checksum
1497+
echo "hash-${{ matrix.os }}=$(base64 -w0 checksum || base64 checksum)" >> "${GITHUB_OUTPUT}"
1498+
1499+
provenance:
1500+
needs: [build]
1501+
strategy:
1502+
fail-fast: false # Don't cancel other jobs if one fails
1503+
matrix:
1504+
os: [ubuntu-latest, macos-latest, windows-latest]
1505+
permissions:
1506+
actions: read
1507+
id-token: write
1508+
contents: write
1509+
uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected]
1510+
with:
1511+
base64-subjects: "${{ needs.build.outputs[format('hash-{0}', matrix.os)] }}"
1512+
upload-assets: true # NOTE: This does nothing unless 'upload-tag-name' parameter is also set to an existing tag
1513+
```
1514+
1515+
Alternatively, to generate 3 different provenance statements, one per each
1516+
artifact, you would use the following example:
1517+
1518+
```yaml
1519+
defaults:
1520+
run:
1521+
shell: bash
1522+
1523+
jobs:
1524+
build:
1525+
runs-on: ${{ matrix.os }}
1526+
strategy:
1527+
fail-fast: false # Don't cancel other jobs if one fails
1528+
matrix:
1529+
os: [ubuntu-latest, macos-latest, windows-latest]
1530+
outputs:
1531+
hash-ubuntu-latest: ${{ steps.hash.outputs.hash-ubuntu-latest }}
1532+
hash-macos-latest: ${{ steps.hash.outputs.hash-macos-latest }}
1533+
hash-windows-latest: ${{ steps.hash.outputs.hash-windows-latest }}
1534+
steps:
1535+
- run: git config --global core.autocrlf input
1536+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
1537+
with:
1538+
persist-credentials: false
1539+
1540+
# Do the build to create release_artifact_${{ runner.os }}
1541+
- run: ...
1542+
1543+
- uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
1544+
with:
1545+
path: release_artifact_${{ runner.os }}
1546+
name: release_artifact_${{ runner.os }}
1547+
if-no-files-found: error
1548+
- id: hash
1549+
run: |
1550+
set -euo pipefail
1551+
(sha256sum -t release_artifact_${{ runner.os }} || shasum -a 256 release_artifact_${{ runner.os }}) > checksum
1552+
echo "hash-${{ matrix.os }}=$(base64 -w0 checksum || base64 checksum)" >> "${GITHUB_OUTPUT}"
1553+
1554+
combine_hashes:
1555+
needs: [build]
1556+
runs-on: ubuntu-latest
1557+
outputs:
1558+
hashes: ${{ steps.combine_hashes.outputs.hashes }}
1559+
env:
1560+
HASHES: ${{ toJSON(needs.build.outputs) }}
1561+
steps:
1562+
- id: combine_hashes
1563+
run: |
1564+
set -euo pipefail
1565+
echo "${HASHES}" | jq -r '.[] | @base64d' | sed "/^$/d" > hashes
1566+
echo "hashes=$(base64 -w0 hashes)" >> "${GITHUB_OUTPUT}"
1567+
1568+
provenance:
1569+
needs: [combine_hashes]
1570+
permissions:
1571+
actions: read
1572+
id-token: write
1573+
contents: write
1574+
uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected]
1575+
with:
1576+
base64-subjects: "${{ needs.combine_hashes.outputs.hashes }}"
1577+
upload-assets: true # NOTE: This does nothing unless 'upload-tag-name' parameter is also set to an existing tag
1578+
1579+
```
1580+
14111581
## Known Issues
14121582

14131583
### Skip output 'hashes' since it may contain secret

0 commit comments

Comments
 (0)