Skip to content

Release pipeline

Release pipeline #161

Workflow file for this run

name: Release pipeline
on:
push:
# only for version 2.x.x releases and release candidates
tags:
- v2.?.?
- v2.?.?-RC?
workflow_dispatch:
env:
# threatdragon is the working area on docker hub so use this area
# owasp/threat-dragon is the final release area so DO NOT use that
IMAGE_NAME: threatdragon/owasp-threat-dragon
# for security reasons the github actions are pinned to specific release versions
jobs:
site_unit_tests:
name: Site unit tests
runs-on: ubuntu-24.04
defaults:
run:
working-directory: td.vue
steps:
- name: Checkout
uses: actions/checkout@v4.2.0
- name: Use node LTS 20.14.0
uses: actions/setup-node@v4.1.0
with:
node-version: '20.14.0'
- name: Cache NPM dir
uses: actions/cache@v4.1.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
${{ runner.os }}-
- name: Install packages
run: npm clean-install
- name: lint
run: npm run lint
- name: Unit test
run: npm run test:unit
server_unit_tests:
name: Server unit tests
runs-on: ubuntu-24.04
defaults:
run:
working-directory: td.server
steps:
- name: Checkout
uses: actions/checkout@v4.2.0
- name: Use node LTS 20.14.0
uses: actions/setup-node@v4.1.0
with:
node-version: '20.14.0'
- name: Cache NPM dir
uses: actions/cache@v4.1.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
${{ runner.os }}-
- name: Install packages
run: npm clean-install
- name: lint
run: npm run lint
- name: Unit test
run: npm run test:unit
- name: Create server SBOM
run: npm run make-sbom
- name: Save SBOM artifact
uses: actions/upload-artifact@v4.4.0
with:
name: sboms-server
path: './td.server/sbom.*'
include-hidden-files: true
desktop_unit_tests:
name: Desktop unit tests
runs-on: ubuntu-24.04
defaults:
run:
working-directory: td.vue
steps:
- name: Checkout
uses: actions/checkout@v4.2.0
- name: Use node LTS 20.14.0
uses: actions/setup-node@v4.1.0
with:
node-version: '20.14.0'
- name: Cache NPM dir
uses: actions/cache@v4.1.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
${{ runner.os }}-
- name: Install packages
run: npm clean-install
- name: lint
run: npm run lint:desktop
- name: Unit test
run: npm run test:desktop
desktop_windows:
# Build only Windows installer and provide to github Release Draft
name: Windows installer
runs-on: windows-latest
needs: [desktop_unit_tests, site_unit_tests]
defaults:
run:
working-directory: td.vue
steps:
- name: Check out
uses: actions/checkout@v4.2.0
- name: Use node LTS 20.14.0
uses: actions/setup-node@v4.1.0
with:
node-version: '20.14.0'
- name: Cache NPM dir
uses: actions/cache@v4.1.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
${{ runner.os }}-
- name: Install clean packages
run: npm clean-install
- name: Build Windows executable
# code signing done later using Extended Verification (EV) with a hardware key
run: npm run build:desktop -- --windows --publish never
- name: Save SBOM artifact
uses: actions/upload-artifact@v4.4.0
with:
name: sboms-desktop-windows-site
path: './td.vue/dist-desktop/bundled/.sbom/*'
include-hidden-files: true
if-no-files-found: error
desktop_macos:
# Build and publish MacOS installer to github Release Draft
# the draft name uses version and is created if it does not already exist
name: MacOS installer
runs-on: macos-latest
needs: [desktop_unit_tests, site_unit_tests]
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# MacOS signing: certificate and password, see electron.build/code-signing
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
CSC_LINK: ${{ secrets.MAC_CERTS }}
# MacOS notarization: see github.com/electron/notarize#readme
API_KEY_ID: ${{ secrets.API_KEY_ID }}
API_KEY_ISSUER_ID: ${{ secrets.API_KEY_ISSUER_ID }}
# APPLE_ID: ${{ secrets.APPLE_ID }}
# APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
# APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
defaults:
run:
working-directory: td.vue
steps:
- name: Check out
uses: actions/checkout@v4.2.0
- name: Use node LTS 20.14.0
uses: actions/setup-node@v4.1.0
with:
node-version: '20.14.0'
- name: Cache NPM dir
uses: actions/cache@v4.1.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
${{ runner.os }}-
- name: Install clean packages
run: npm clean-install
- name: Prepare for MacOS notarization
# Import Apple API key to support signing of app
run: |
mkdir -p ~/private_keys/
echo '${{ secrets.API_KEY }}' > ~/private_keys/AuthKey_${{ secrets.API_KEY_ID }}.p8
- name: Publish MacOS disk image
run: npm run build:desktop -- --mac --publish always
- name: Print logs on error
if: ${{ failure() }}
run: find . -name "*.log" -exec cat '{}' \; -print
- name: Save SBOM artifact
uses: actions/upload-artifact@v4.4.0
with:
name: sboms-desktop-macos-site
path: './td.vue/dist-desktop/bundled/.sbom/*'
include-hidden-files: true
if-no-files-found: error
desktop_linux:
name: Linux installers
runs-on: ubuntu-24.04
needs: [desktop_unit_tests, site_unit_tests]
defaults:
run:
working-directory: td.vue
steps:
- name: Check out
uses: actions/checkout@v4.2.0
- name: Use node LTS 20.14.0
uses: actions/setup-node@v4.1.0
with:
node-version: '20.14.0'
- name: Cache NPM dir
uses: actions/cache@v4.1.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
${{ runner.os }}-
- name: Install clean packages
run: npm clean-install
# Build and publish Linux installers to github Release Draft
# for all linux images EXCEPT for the snap
# Snaps do not publish, even with snapcraft installed, so use Snap Store
- name: Publish Linux app images
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npm run build:desktop -- --linux AppImage deb rpm --publish always
- name: Print logs on error
if: ${{ failure() }}
run: find . -name "*.log" -exec cat '{}' \; -print
- name: Save SBOM artifact
uses: actions/upload-artifact@v4.4.0
with:
name: sboms-desktop-linux-site
path: './td.vue/dist-desktop/bundled/.sbom/*'
include-hidden-files: true
if-no-files-found: error
desktop_linux_snap:
name: Linux snap
runs-on: ubuntu-24.04
needs: [desktop_unit_tests, site_unit_tests]
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }}
defaults:
run:
working-directory: td.vue
steps:
- name: Check out
uses: actions/checkout@v4.2.0
- name: Use node LTS 20.14.0
uses: actions/setup-node@v4.1.0
with:
node-version: '20.14.0'
- name: Cache NPM dir
uses: actions/cache@v4.1.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
${{ runner.os }}-
- name: Install clean packages
run: |
sudo snap install snapcraft --classic
npm clean-install
# Build the snap, but do not use inbuilt publish
# Snaps do not publish, even with snapcraft installed, so use Snap Store
- name: Build Linux snap
shell: bash
run: npm run build:desktop -- --linux snap
- name: Upload to Snap Store
shell: bash
run: snapcraft upload --release=stable dist-desktop/threat-dragon*.snap
- name: Print logs on error
if: ${{ failure() }}
run: find . -name "*.log" -exec cat '{}' \; -print
- name: Save SBOM artifact
uses: actions/upload-artifact@v4.4.0
with:
name: sboms-desktop-linux-snap-site
path: './td.vue/dist-desktop/bundled/.sbom/*'
include-hidden-files: true
if-no-files-found: error
dockerhub_release:
name: Publish to dockerhub
runs-on: ubuntu-24.04
needs: [server_unit_tests, site_unit_tests]
steps:
- name: Checkout
uses: actions/checkout@v4.2.0
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.2.0
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3.7.0
with:
install: true
- name: Cache Docker layers
uses: actions/cache@v4.1.1
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ hashFiles('Dockerfile') }}
restore-keys: |
${{ runner.os }}-buildx-
${{ runner.os }}-
- name: Login to Docker Hub
uses: docker/login-action@v3.3.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# platform manifests not (yet) supported, so split out architectures
- name: Build for amd64 and push to Docker Hub
id: docker_build_amd64
uses: docker/build-push-action@v6.10.0
with:
context: ./
file: ./Dockerfile
builder: ${{ steps.buildx.outputs.name }}
push: ${{ startsWith(github.ref, 'refs/tags/v') }}
tags: ${{ env.IMAGE_NAME }}:${{ github.ref_name }},${{ env.IMAGE_NAME }}:stable
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache,mode=max
platforms: linux/amd64
load: true
- name: Build for arm64 and push to Docker Hub
id: docker_build_arm64
uses: docker/build-push-action@v6.10.0
with:
context: ./
file: ./Dockerfile
builder: ${{ steps.buildx.outputs.name }}
push: ${{ startsWith(github.ref, 'refs/tags/v') }}
tags: ${{ env.IMAGE_NAME }}:${{ github.ref_name }}-arm64
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
platforms: linux/arm64
load: true
- name: fetch app SBOM
run: docker run --rm --entrypoint tar "$IMAGE_ID" -c boms | tar -xv
env:
IMAGE_ID: ${{ steps.docker_build_amd64.outputs.imageid }}
- name: Save SBOM artifact
uses: actions/upload-artifact@v4.4.0
with:
name: sboms-container-image-app
path: './boms/*'
include-hidden-files: true
if-no-files-found: error
- # Temp fix for large cache bug
# https://github.com/docker/build-push-action/issues/252
name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
sbom_combiner:
name: SBOM combiner
runs-on: ubuntu-24.04
needs:
- server_unit_tests
- desktop_macos
- desktop_linux
- desktop_linux_snap
- desktop_windows
- dockerhub_release
steps:
- name: Fetch prepared SBOM artifacts
uses: actions/download-artifact@v4.1.7
with:
pattern: 'sboms-*'
merge-multiple: false
path: 'raw/'
- name: Fetch SBOMs
run: |
set -eux
mkdir -p sboms/threat-dragon-container-image/app/
cp raw/sboms-server/sbom.json sboms/threat-dragon-server-bom.json
cp raw/sboms-server/sbom.xml sboms/threat-dragon-server-bom.xml
cp raw/sboms-desktop-windows-site/bom.json sboms/threat-dragon-desktop-windows-site-bom.json
cp raw/sboms-desktop-windows-site/bom.xml sboms/threat-dragon-desktop-windows-site-bom.xml
cp raw/sboms-desktop-macos-site/bom.json sboms/threat-dragon-desktop-macos-site-bom.json
cp raw/sboms-desktop-macos-site/bom.xml sboms/threat-dragon-desktop-macos-site-bom.xml
cp raw/sboms-desktop-linux-site/bom.json sboms/threat-dragon-desktop-linux-site-bom.json
cp raw/sboms-desktop-linux-site/bom.xml sboms/threat-dragon-desktop-linux-site-bom.xml
cp raw/sboms-desktop-linux-snap-site/bom.json sboms/threat-dragon-desktop-linux-snap-site-bom.json
cp raw/sboms-desktop-linux-snap-site/bom.xml sboms/threat-dragon-desktop-linux-snap-site-bom.xml
cp raw/sboms-container-image-app/* sboms/threat-dragon-container-image/app/
- name: Save SBOM artifact
uses: actions/upload-artifact@v4.4.0
with:
name: sboms
path: 'sboms/'
include-hidden-files: true
webapp_release:
name: Publish web application
runs-on: ubuntu-24.04
needs:
- desktop_macos
- desktop_linux
- desktop_windows
- sbom_combiner
steps:
- name: Check out
uses: actions/checkout@v4.2.0
- name: Fetch prepared SBOM artifacts
uses: actions/download-artifact@v4.1.8
with:
name: 'sboms'
path: 'sboms/'
- name: Prepare release notes
run: |
releaseVersion=${{ github.ref_name }}
sed -e s/2.x.x/${releaseVersion:1}/g .release-note-template.md > ./release-notes.txt
tar -czvf threat-dragon-sboms.zip sboms
- name: Create release notes
uses: softprops/action-gh-release@v2.1.0
with:
draft: true
name: "${releaseVersion:1}"
append_body: true
body_path: ./release-notes.txt
generate_release_notes: true
files: |
threat-dragon-sboms.zip