Skip to content

Commit 5cd033a

Browse files
Merge pull request #153 from DefGuard/sbom-into-main
Merge SBOM CI pipelines into main
2 parents 03ff050 + 9427c90 commit 5cd033a

5 files changed

Lines changed: 192 additions & 20 deletions

File tree

.github/workflows/build.yaml

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ on:
66
- dev
77
- 'release/**'
88
- 'hotfix/**'
9+
tags:
10+
- v*.*.*
911
paths-ignore:
1012
- "*.md"
1113
- "LICENSE"
@@ -53,22 +55,25 @@ jobs:
5355
- name: Build iOS
5456
run: flutter build ipa --release --obfuscate --split-debug-info=build/debug-info --build-number=${{ github.run_number }}
5557

56-
# Temporarily disable uploading artifacts until quota gets recalculated.
57-
# Error: Failed to CreateArtifact: Artifact storage quota has been hit. Unable to upload any new artifacts. Usage is recalculated every 6-12 hours.
58-
# - name: Upload iOS artifact
59-
# uses: actions/upload-artifact@v4
60-
# with:
61-
# name: ios
62-
# retention-days: 3
63-
# path: client/build/ios/ipa/*.ipa
64-
6558
- name: Upload app to TestFlight
6659
uses: apple-actions/upload-testflight-build@v3
60+
# Mobile applications are published to the App Store manually, with release tags applied
61+
# post-publication. To avoid redundant uploads, this step executes only for non-tagged
62+
# builds, ensuring tagged releases are distributed exclusively to GitHub.
63+
if: "!startsWith(github.ref, 'refs/tags/')"
6764
with:
6865
app-path: "client/build/ios/ipa/Defguard.ipa"
6966
issuer-id: ${{ secrets.API_ISSUER_ID }}
7067
api-key-id: ${{ secrets.ASC_API_KEY_ID }}
7168
api-private-key: ${{ secrets.PRIVATE_KEY_CONTENTS }}
69+
70+
- name: Upload iOS Artifact
71+
uses: actions/upload-artifact@v4
72+
if: startsWith(github.ref, 'refs/tags/')
73+
with:
74+
name: ios-app
75+
path: "client/build/ios/ipa/Defguard.ipa"
76+
retention-days: 2
7277

7378
build-android:
7479
runs-on: [self-hosted, macOS]
@@ -114,19 +119,30 @@ jobs:
114119
keyStorePassword: "${{ secrets.ANDROID_KEYSTORE_PASSWORD }}"
115120
keyPassword: "${{ secrets.ANDROID_KEYSTORE_PASSWORD }}"
116121

117-
# Temporarily disable uploading artifacts until quota gets recalculated.
118-
# Error: Failed to CreateArtifact: Artifact storage quota has been hit. Unable to upload any new artifacts. Usage is recalculated every 6-12 hours.
119-
# - name: Upload android artifact
120-
# uses: actions/upload-artifact@v4
121-
# with:
122-
# name: android
123-
# retention-days: 3
124-
# path: client/build/app/outputs/bundle/release/app-release.aab
125-
126122
- name: Publish to Play Store
127123
uses: r0adkll/upload-google-play@v1
124+
# Mobile applications are published to the Play Store manually, with release tags applied
125+
# post-publication. To avoid redundant uploads, this step executes only for non-tagged
126+
# builds, ensuring tagged releases are distributed exclusively to GitHub.
127+
if: "!startsWith(github.ref, 'refs/tags/')"
128128
with:
129129
serviceAccountJsonPlainText: "${{ secrets.ANDROID_SERVICE_ACCOUNT_JSON }}"
130130
packageName: net.defguard.mobile
131131
releaseFiles: client/build/app/outputs/bundle/release/app-release.aab
132132
track: internal
133+
134+
- name: Upload Android Artifact
135+
uses: actions/upload-artifact@v4
136+
if: startsWith(github.ref, 'refs/tags/')
137+
with:
138+
name: android-app
139+
path: "client/build/app/outputs/bundle/release/app-release.aab"
140+
retention-days: 2
141+
142+
release:
143+
needs: [build-ios, build-android]
144+
# Create release only if CI was triggered by a tag.
145+
if: startsWith(github.ref, 'refs/tags/')
146+
uses: ./.github/workflows/release.yaml
147+
secrets:
148+
PRIVATE_REPO_CLONING_TOKEN: ${{ secrets.PRIVATE_REPO_CLONING_TOKEN }}

.github/workflows/lint-and-test.yaml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ on:
2121

2222
jobs:
2323
lint:
24-
name: Lint project
25-
runs-on: [self-hosted, macOS]
24+
name: Lint and test
25+
runs-on: [self-hosted, Linux, X64]
2626
defaults:
2727
run:
2828
working-directory: ./client
@@ -31,6 +31,16 @@ jobs:
3131
- name: Checkout
3232
uses: actions/checkout@v4
3333

34+
- name: Scan code with Trivy
35+
uses: aquasecurity/trivy-action@0.33.1
36+
with:
37+
scan-type: 'fs'
38+
scan-ref: '.'
39+
exit-code: "1"
40+
ignore-unfixed: true
41+
severity: "CRITICAL,HIGH,MEDIUM"
42+
scanners: "vuln"
43+
3444
- name: setup flutter
3545
uses: subosito/flutter-action@v2
3646
with:

.github/workflows/release.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: "Release"
2+
3+
on:
4+
workflow_call:
5+
secrets:
6+
PRIVATE_REPO_CLONING_TOKEN:
7+
description: "Cloning token"
8+
required: true
9+
10+
jobs:
11+
create-release:
12+
runs-on: self-hosted
13+
outputs:
14+
upload_url: ${{ steps.release.outputs.upload_url }}
15+
16+
steps:
17+
- name: Download build artifacts
18+
uses: actions/download-artifact@v5
19+
with:
20+
path: ./artifacts
21+
merge-multiple: true
22+
23+
- name: Create GitHub release
24+
id: release
25+
uses: softprops/action-gh-release@v2
26+
with:
27+
draft: true
28+
files: |
29+
./artifacts/Defguard.ipa
30+
./artifacts/app-release.aab
31+
32+
create-sbom:
33+
needs: [create-release]
34+
uses: ./.github/workflows/sbom.yaml
35+
with:
36+
upload_url: ${{ needs.create-release.outputs.upload_url }}
37+
secrets:
38+
PRIVATE_REPO_CLONING_TOKEN: ${{ secrets.PRIVATE_REPO_CLONING_TOKEN }}
39+
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Periodic SBOM Regeneration
2+
3+
on:
4+
schedule:
5+
- cron: '30 2 * * *' # 2:30 AM UTC
6+
7+
jobs:
8+
list-releases:
9+
name: List releases
10+
runs-on: ubuntu-latest
11+
outputs:
12+
releases: ${{ steps.get-releases.outputs.releases }}
13+
steps:
14+
- name: Get list of releases
15+
id: get-releases
16+
env:
17+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18+
run: |
19+
RELEASES_JSON=$(gh api repos/${{ github.repository }}/releases \
20+
--jq '[.[]
21+
| select(.draft == false and (.tag_name | test("^v[0-9]+\\.[0-9]+\\.[0-9]+$")))
22+
| {tagName: .tag_name, uploadUrl: .upload_url}][:1]')
23+
echo "releases=$RELEASES_JSON" >> $GITHUB_OUTPUT
24+
25+
regenerate-for-release:
26+
name: Regenerate SBOM for release
27+
needs: list-releases
28+
# Don't run if no releases were found.
29+
if: needs.list-releases.outputs.releases != '[]'
30+
strategy:
31+
fail-fast: false
32+
matrix:
33+
release: ${{ fromJson(needs.list-releases.outputs.releases) }}
34+
uses: ./.github/workflows/sbom.yaml
35+
with:
36+
upload_url: ${{ matrix.release.uploadUrl }}
37+
tag: ${{ matrix.release.tagName }}
38+
secrets:
39+
PRIVATE_REPO_CLONING_TOKEN: ${{ secrets.PRIVATE_REPO_CLONING_TOKEN }}

.github/workflows/sbom.yaml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: Create SBOM files
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
upload_url:
7+
description: "Release assets upload URL"
8+
required: true
9+
type: string
10+
tag:
11+
description: "The git tag to generate SBOM for - used in scheduled runs"
12+
required: false
13+
type: string
14+
secrets:
15+
PRIVATE_REPO_CLONING_TOKEN:
16+
description: "Cloning token"
17+
required: true
18+
19+
jobs:
20+
create-sbom:
21+
runs-on: [self-hosted, Linux, X64]
22+
23+
steps:
24+
- name: Determine release tag and version
25+
id: vars
26+
# Uses inputs.tag for scheduled runs, otherwise github.ref_name.
27+
run: |
28+
TAG_NAME=${{ inputs.tag || github.ref_name }}
29+
VERSION=${TAG_NAME#v}
30+
echo "TAG_NAME=$TAG_NAME" >> $GITHUB_OUTPUT
31+
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
32+
33+
- name: Checkout
34+
uses: actions/checkout@v4
35+
with:
36+
submodules: recursive
37+
ref: ${{ steps.vars.outputs.TAG_NAME }}
38+
token: ${{ secrets.PRIVATE_REPO_CLONING_TOKEN }}
39+
40+
- name: Create SBOM with Trivy
41+
uses: aquasecurity/trivy-action@0.33.1
42+
with:
43+
scan-type: 'fs'
44+
format: 'spdx-json'
45+
output: "defguard-mobile-${{ steps.vars.outputs.VERSION }}.sbom.json"
46+
scan-ref: '.'
47+
severity: "CRITICAL,HIGH,MEDIUM,LOW"
48+
scanners: "vuln"
49+
50+
- name: Create security advisory file with Trivy
51+
uses: aquasecurity/trivy-action@0.33.1
52+
with:
53+
scan-type: 'fs'
54+
format: 'json'
55+
output: "defguard-mobile-${{ steps.vars.outputs.VERSION }}.advisories.json"
56+
scan-ref: '.'
57+
severity: "CRITICAL,HIGH,MEDIUM,LOW"
58+
scanners: "vuln"
59+
60+
- name: Upload SBOMs and advisories
61+
uses: shogo82148/actions-upload-release-asset@v1
62+
env:
63+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
64+
with:
65+
upload_url: ${{ inputs.upload_url }}
66+
asset_path: "defguard-*.json"
67+
asset_content_type: application/octet-stream
68+
overwrite: true

0 commit comments

Comments
 (0)