Skip to content

Commit b6def06

Browse files
authored
Cross platform test and UTF-8 fixes (#1553)
* workflows: Add a cross-OS test workflow Signed-off-by: Jussi Kukkonen <jkukkonen@google.com> * cli: Always write utf-8 Apparently Python really likes to use cp1252 on some platforms but our bundles must not be platform specific. Signed-off-by: Jussi Kukkonen <jkukkonen@google.com> * cli: read signature as utf too Signed-off-by: Jussi Kukkonen <jkukkonen@google.com> * Avoid git line ending problems in cross-platform test Signed-off-by: Jussi Kukkonen <jkukkonen@google.com> --------- Signed-off-by: Jussi Kukkonen <jkukkonen@google.com>
1 parent d5f3a50 commit b6def06

4 files changed

Lines changed: 96 additions & 9 deletions

File tree

.github/workflows/cross-os.yml

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Copyright 2025 The Sigstore Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: Cross-platform sign and verify
16+
on:
17+
push:
18+
branches:
19+
- main
20+
- series/*
21+
pull_request:
22+
workflow_dispatch:
23+
24+
permissions: {}
25+
26+
defaults:
27+
run:
28+
shell: bash
29+
30+
jobs:
31+
sign:
32+
name: Sign on ${{ matrix.os }}
33+
runs-on: ${{ matrix.os }}-latest
34+
strategy:
35+
fail-fast: false
36+
matrix:
37+
os: [ubuntu, macos, windows]
38+
steps:
39+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
40+
with:
41+
persist-credentials: false
42+
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
43+
with:
44+
python-version: "3.x"
45+
cache: "pip"
46+
cache-dependency-path: pyproject.toml
47+
- run: pip install .
48+
- name: Fetch testing oidc token
49+
uses: sigstore-conformance/extremely-dangerous-public-oidc-beacon@b517a742e5a3db9e3cbf8e2e1c792b36982f78db # main
50+
- name: Sign
51+
run: python -m sigstore --staging sign --identity-token $(cat oidc-token.txt) test/assets/a.txt
52+
- name: upload signature bundle
53+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
54+
with:
55+
name: ${{ matrix.os }}-bundle
56+
path: test/assets/a.txt.sigstore.json
57+
if-no-files-found: error
58+
retention-days: 1
59+
verify:
60+
name: Verify ${{ matrix.signed-with-os }} bundle on ${{ matrix.os }}
61+
if: ${{ always() }} # don't stop some verification if one of the signing jobs failed
62+
needs: [sign]
63+
runs-on: ${{ matrix.os }}-latest
64+
strategy:
65+
fail-fast: false # Don't cancel other jobs if one fails
66+
matrix:
67+
os: [ubuntu, macos, windows]
68+
signed-with-os: [ubuntu, macos, windows]
69+
steps:
70+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
71+
with:
72+
persist-credentials: false
73+
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
74+
with:
75+
python-version: "3.x"
76+
cache: "pip"
77+
cache-dependency-path: pyproject.toml
78+
- run: pip install .
79+
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
80+
with:
81+
name: ${{ matrix.signed-with-os }}-bundle
82+
- name: Verify
83+
run: |
84+
python -m sigstore --staging verify github --verbose \
85+
--cert-identity "https://github.com/sigstore-conformance/extremely-dangerous-public-oidc-beacon/.github/workflows/extremely-dangerous-oidc-beacon.yml@refs/heads/main" \
86+
--bundle a.txt.sigstore.json \
87+
test/assets/a.txt

sigstore/_cli.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -686,16 +686,16 @@ def _sign_file_threaded(
686686

687687
if outputs.signature is not None:
688688
signature = base64.b64encode(result.signature).decode()
689-
with outputs.signature.open(mode="w") as io:
689+
with outputs.signature.open(mode="w", encoding="utf-8") as io:
690690
print(signature, file=io)
691691

692692
if outputs.certificate is not None:
693693
cert_pem = signer._signing_cert().public_bytes(Encoding.PEM).decode()
694-
with outputs.certificate.open(mode="w") as io:
694+
with outputs.certificate.open(mode="w", encoding="utf-8") as io:
695695
print(cert_pem, file=io)
696696

697697
if outputs.bundle is not None:
698-
with outputs.bundle.open(mode="w") as io:
698+
with outputs.bundle.open(mode="w", encoding="utf-8") as io:
699699
print(result.to_json(), file=io)
700700

701701

@@ -769,7 +769,7 @@ def _attest(args: argparse.Namespace) -> None:
769769
_invalid_arguments(args, f"Predicate must be a file: {predicate_path}")
770770

771771
try:
772-
with open(predicate_path, "r") as f:
772+
with open(predicate_path, "r", encoding="utf-8") as f:
773773
predicate = json.load(f)
774774
# We do a basic sanity check using our Pydantic models to see if the
775775
# contents of the predicate file match the specified predicate type.
@@ -1067,7 +1067,7 @@ def _collect_verification_state(
10671067

10681068
# Load the signature
10691069
_logger.debug(f"Using signature from: {materials.signature}")
1070-
b64_signature = materials.signature.read_text()
1070+
b64_signature = materials.signature.read_text(encoding="utf-8")
10711071
signature = base64.b64decode(b64_signature)
10721072

10731073
# When using "detached" materials, we *must* retrieve the log

test/integration/cli/test_attest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def test_attest_success_default_output_bundle(
7373

7474
assert expected_output_bundle.exists()
7575
verifier = Verifier.staging()
76-
with open(expected_output_bundle, "r") as bundle_file:
76+
with open(expected_output_bundle, "r", encoding="utf-8") as bundle_file:
7777
bundle = Bundle.from_json(bundle_file.read())
7878
verifier.verify_dsse(bundle=bundle, policy=UnsafeNoOp())
7979

test/integration/cli/test_sign.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def test_sign_success_default_output_bundle(
7272
assert expected_output_bundle.exists()
7373
verifier = Verifier.staging()
7474
with (
75-
open(expected_output_bundle, "r") as bundle_file,
75+
open(expected_output_bundle, "r", encoding="utf-8") as bundle_file,
7676
open(artifact, "rb") as input_file,
7777
):
7878
bundle = Bundle.from_json(bundle_file.read())
@@ -112,7 +112,7 @@ def test_sign_success_multiple_artifacts(capsys, sigstore, asset_integration, tm
112112
assert expected_output_bundle.exists()
113113
verifier = Verifier.staging()
114114
with (
115-
open(expected_output_bundle, "r") as bundle_file,
115+
open(expected_output_bundle, "r", encoding="utf-8") as bundle_file,
116116
open(artifact, "rb") as input_file,
117117
):
118118
bundle = Bundle.from_json(bundle_file.read())
@@ -154,7 +154,7 @@ def test_sign_success_multiple_artifacts_rekor_v2(
154154
assert expected_output_bundle.exists()
155155
verifier = Verifier.staging()
156156
with (
157-
open(expected_output_bundle, "r") as bundle_file,
157+
open(expected_output_bundle, "r", encoding="utf-8") as bundle_file,
158158
open(artifact, "rb") as input_file,
159159
):
160160
bundle = Bundle.from_json(bundle_file.read())

0 commit comments

Comments
 (0)