@@ -3,63 +3,148 @@ name: Release
33on :
44 push :
55 tags :
6- - ' v*'
6+ - ' v*'
77 workflow_dispatch :
88 inputs :
99 version :
1010 description : ' Release version (e.g., v1.0.0)'
1111 required : false
1212 type : string
1313
14+ permissions :
15+ contents : write
16+ attestations : write
17+ id-token : write
18+
1419jobs :
1520 build-release :
1621 runs-on : macos-26
1722
1823 steps :
19- - name : Checkout code
20- uses : actions/checkout@v4
21-
22- - name : Setup Swift
23- uses : maartene/setup-swift@main
24- with :
25- swift-version : ' 6.2'
26-
27- - name : Build Release
28- run : swift build -c release
29-
30- - name : Create Release
31- if : github.event_name == 'push'
32- uses : softprops/action-gh-release@v1
33- with :
34- name : ${{ github.ref_name }}
35- draft : false
36- prerelease : ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}
37- files : |
38- .build/release/container-compose
39- env :
40- GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
41-
42- - name : Upload to Release
43- if : github.event_name == 'workflow_dispatch'
44- run : |
45- # Create release if it doesn't exist
46- VERSION="${{ github.inputs.version || 'latest' }}"
47- TAG="v${VERSION#v}"
48-
49- # Create or get release ID
50- RELEASE_ID=$(gh api repos/${{ github.repository }}/releases/tags/$TAG --jq '.id' 2>/dev/null || echo "")
51-
52- if [ -z "$RELEASE_ID" ]; then
53- RELEASE_ID=$(gh api repos/${{ github.repository }}/releases -X POST \
54- --field tag_name="$TAG" \
55- --field name="$TAG" \
56- --field draft=false \
57- --jq '.id')
58- fi
59-
60- # Upload asset
61- gh api repos/${{ github.repository }}/releases/$RELEASE_ID/assets \
62- -F "file=@.build/release/container-compose" \
63- -F "name=container-compose"
64- env :
65- GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
24+ - name : Checkout code
25+ uses : actions/checkout@v4
26+
27+ - name : Setup Swift
28+ uses : maartene/setup-swift@main
29+ with :
30+ swift-version : ' 6.2'
31+
32+ - name : Build Release
33+ run : swift build -c release
34+
35+ - name : Generate SHA256 Checksum
36+ id : checksum
37+ run : |
38+ shasum -a 256 .build/release/container-compose | awk '{print $1}' > .build/release/container-compose.sha256
39+ echo "sha256=$(cat .build/release/container-compose.sha256)" >> $GITHUB_OUTPUT
40+ echo "Binary SHA256: $(cat .build/release/container-compose.sha256)"
41+
42+ - name : Attest Build Provenance
43+ uses : actions/attest-build-provenance@v1
44+ with :
45+ subject-path : ' .build/release/container-compose'
46+
47+ - name : Generate Verification Instructions
48+ run : |
49+ cat > .build/release/VERIFICATION.md << 'EOF'
50+ # Binary Verification
51+
52+ ## SHA256 Checksum
53+ ```
54+ ${{ steps.checksum.outputs.sha256 }} container-compose
55+ ```
56+
57+ ## Verify Download
58+ ```bash
59+ # macOS/Linux
60+ shasum -a 256 ./container-compose | grep ${{ steps.checksum.outputs.sha256 }}
61+
62+ # Should output matching hash
63+ ```
64+
65+ ## Build Reproducibility
66+ This binary was built with:
67+ - macOS 26.x
68+ - Xcode 26.3
69+ - Swift 6.2
70+ - Apple Silicon (arm64)
71+
72+ ## Attestation
73+ This release includes cryptographic attestation via GitHub Actions.
74+ Verify attestation: `gh attestation verify container-compose --owner explicitcontextualunderstanding`
75+
76+ ## Source Code
77+ Tagged commit: ${{ github.sha }}
78+ Compare: https://github.com/${{ github.repository }}/compare/${{ github.ref_name }}
79+ EOF
80+
81+ - name : Create Release with Verification
82+ if : github.event_name == 'push'
83+ uses : softprops/action-gh-release@v1
84+ with :
85+ name : ${{ github.ref_name }}
86+ draft : false
87+ prerelease : ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}
88+ files : |
89+ .build/release/container-compose
90+ .build/release/container-compose.sha256
91+ .build/release/VERIFICATION.md
92+ body : |
93+ ## Verification
94+
95+ **SHA256:** `${{ steps.checksum.outputs.sha256 }}`
96+
97+ Verify your download:
98+ ```bash
99+ shasum -a 256 ./container-compose | grep ${{ steps.checksum.outputs.sha256 }}
100+ ```
101+
102+ **Build Attestation:** This release includes cryptographic attestation.
103+ ```bash
104+ gh attestation verify container-compose --owner explicitcontextualunderstanding
105+ ```
106+
107+ See VERIFICATION.md for full details.
108+ env :
109+ GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
110+
111+ - name : Upload to Release (workflow_dispatch)
112+ if : github.event_name == 'workflow_dispatch'
113+ run : |
114+ # Create release if it doesn't exist
115+ VERSION="${{ github.inputs.version || 'latest' }}"
116+ TAG="v${VERSION#v}"
117+
118+ # Create or get release ID
119+ RELEASE_ID=$(gh api repos/${{ github.repository }}/releases/tags/$TAG --jq '.id' 2>/dev/null || echo "")
120+
121+ if [ -z "$RELEASE_ID" ]; then
122+ RELEASE_ID=$(gh api repos/${{ github.repository }}/releases -X POST \
123+ --field tag_name="$TAG" \
124+ --field name="$TAG" \
125+ --field draft=false \
126+ --field body="## Verification
127+
128+ **SHA256:** \`${{ steps.checksum.outputs.sha256 }}\`
129+
130+ Verify your download:
131+ \`\`\`bash
132+ shasum -a 256 ./container-compose | grep ${{ steps.checksum.outputs.sha256 }}
133+ \`\`\`
134+
135+ See full verification instructions in VERIFICATION.md" \
136+ --jq '.id')
137+ fi
138+
139+ # Upload assets
140+ gh api repos/${{ github.repository }}/releases/$RELEASE_ID/assets \
141+ -F "file=@.build/release/container-compose" \
142+ -F "name=container-compose"
143+ gh api repos/${{ github.repository }}/releases/$RELEASE_ID/assets \
144+ -F "file=@.build/release/container-compose.sha256" \
145+ -F "name=container-compose.sha256"
146+ gh api repos/${{ github.repository }}/releases/$RELEASE_ID/assets \
147+ -F "file=@.build/release/VERIFICATION.md" \
148+ -F "name=VERIFICATION.md"
149+ env :
150+ GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
0 commit comments