Skip to content

Commit cc189db

Browse files
Merge pull request #25 from NHSDigital/DTOSS-12157-create-application-package-script
[DTOSS-12157] - feat: implement automated release pipeline with CI/CD optimisation
2 parents 08df5a9 + fad91c8 commit cc189db

15 files changed

Lines changed: 2195 additions & 29 deletions

.github/workflows/cicd-2-main-branch.yaml

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ on:
44
push:
55
branches:
66
- main
7+
tags:
8+
- 'v*'
79
workflow_dispatch:
810

911
concurrency:
1012
group: cicd-${{ github.ref }}
1113
cancel-in-progress: true
1214

1315
permissions:
14-
contents: read
16+
contents: write
1517
id-token: write
1618
attestations: write
1719
security-events: write
@@ -26,7 +28,73 @@ jobs:
2628
uses: ./.github/workflows/stage-2-test.yaml
2729
secrets: inherit
2830

29-
build-stage:
31+
release-stage:
3032
needs: test-stage
33+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
34+
runs-on: ubuntu-latest
35+
outputs:
36+
new_release_published: ${{ steps.release.outputs.new_release_published }}
37+
new_release_version: ${{ steps.release.outputs.new_release_version }}
38+
steps:
39+
- name: Checkout code
40+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
41+
with:
42+
fetch-depth: 0
43+
token: ${{ secrets.GITHUB_TOKEN }}
44+
45+
- name: Read tool versions
46+
id: tool-versions
47+
run: |
48+
echo "python=$(awk '/^python / {print $2}' .tool-versions)" >> "$GITHUB_OUTPUT"
49+
echo "uv=$(awk '/^uv / {print $2}' .tool-versions)" >> "$GITHUB_OUTPUT"
50+
51+
- name: Set up Python
52+
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
53+
with:
54+
python-version: ${{ steps.tool-versions.outputs.python }}
55+
56+
- name: Install uv
57+
uses: astral-sh/setup-uv@887a942a15af3a7626099df99e897a18d9e5ab3a # v5.1.0
58+
with:
59+
version: ${{ steps.tool-versions.outputs.uv }}
60+
enable-cache: true
61+
62+
- name: Run semantic-release
63+
id: release
64+
env:
65+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
66+
run: |
67+
uv pip install python-semantic-release --system
68+
69+
# Detect next version (--print exits without side effects)
70+
VERSION=$(semantic-release version --print 2>/dev/null) || true
71+
72+
if [[ -n "$VERSION" ]] && ! git ls-remote --tags origin "refs/tags/v$VERSION" | grep -q .; then
73+
echo "Next version detected: $VERSION"
74+
75+
# Create and push the tag (no commit needed, tag push is not blocked by branch protection)
76+
git tag "v$VERSION"
77+
git push origin "v$VERSION"
78+
79+
echo "new_release_published=true" >> "$GITHUB_OUTPUT"
80+
echo "new_release_version=v$VERSION" >> "$GITHUB_OUTPUT"
81+
else
82+
echo "No new release needed (version: ${VERSION:-none}, tag may already exist)"
83+
echo "new_release_published=false" >> "$GITHUB_OUTPUT"
84+
echo "new_release_version=" >> "$GITHUB_OUTPUT"
85+
fi
86+
87+
build-stage:
88+
needs: [test-stage, release-stage]
89+
if: |
90+
always() &&
91+
needs.test-stage.result == 'success' &&
92+
(
93+
github.ref_type == 'tag' ||
94+
needs.release-stage.outputs.new_release_published == 'true'
95+
)
3196
uses: ./.github/workflows/stage-3-build.yaml
97+
with:
98+
version: ${{ needs.release-stage.outputs.new_release_version }}
99+
new_release_published: ${{ needs.release-stage.outputs.new_release_published == 'true' }}
32100
secrets: inherit

.github/workflows/codeql.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: "CodeQL Analysis"
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
schedule:
7+
- cron: '0 6 * * 1' # Every Monday at 06:00 UTC
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read
12+
security-events: write
13+
14+
jobs:
15+
analyze:
16+
name: CodeQL Analysis
17+
runs-on: ubuntu-latest
18+
strategy:
19+
fail-fast: false
20+
matrix:
21+
language: ['python']
22+
steps:
23+
- name: Checkout repository
24+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
25+
26+
- name: Initialize CodeQL
27+
uses: github/codeql-action/init@8c78abb9b62512e3c45dea6559ffd924ed8549c8 # v3.28.0
28+
with:
29+
languages: ${{ matrix.language }}
30+
31+
- name: Perform CodeQL Analysis
32+
uses: github/codeql-action/analyze@8c78abb9b62512e3c45dea6559ffd924ed8549c8 # v3.28.0

.github/workflows/stage-1-commit.yaml

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ on:
55

66
permissions:
77
contents: read
8-
security-events: write
98

109
jobs:
1110
scan-secrets:
@@ -21,22 +20,3 @@ jobs:
2120

2221
- name: Scan secrets
2322
uses: ./.github/actions/scan-secrets
24-
25-
analyze:
26-
name: CodeQL Analysis
27-
runs-on: ubuntu-latest
28-
strategy:
29-
fail-fast: false
30-
matrix:
31-
language: [ 'python' ]
32-
steps:
33-
- name: Checkout repository
34-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
35-
36-
- name: Initialize CodeQL
37-
uses: github/codeql-action/init@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
38-
with:
39-
languages: ${{ matrix.language }}
40-
41-
- name: Perform CodeQL Analysis
42-
uses: github/codeql-action/analyze@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0

.github/workflows/stage-3-build.yaml

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,19 @@ name: "Stage 3: Build"
22

33
on:
44
workflow_call:
5+
inputs:
6+
version:
7+
description: "Version to build (overrides detection)"
8+
required: false
9+
type: string
10+
new_release_published:
11+
description: "Whether a new release was published by the release stage"
12+
required: false
13+
type: boolean
14+
default: false
515

616
permissions:
7-
contents: read
17+
contents: write
818
id-token: write
919
attestations: write
1020

@@ -15,16 +25,24 @@ jobs:
1525
steps:
1626
- name: Checkout code
1727
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
28+
with:
29+
fetch-depth: 0
1830

1931
- name: Determine version
2032
id: version
2133
run: |
22-
SHORT_HASH="$(git rev-parse --short HEAD)"
23-
echo "short_hash=${SHORT_HASH}" >> "$GITHUB_OUTPUT"
24-
echo "artifact_name=gateway-${SHORT_HASH}.zip" >> "$GITHUB_OUTPUT"
34+
if [[ -n "${{ inputs.version }}" ]]; then
35+
VERSION="${{ inputs.version }}"
36+
elif [[ "${{ github.ref_type }}" == "tag" ]]; then
37+
VERSION="${{ github.ref_name }}"
38+
else
39+
VERSION="$(git rev-parse --short HEAD)"
40+
fi
41+
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
42+
echo "artifact_name=gateway-${VERSION}.zip" >> "$GITHUB_OUTPUT"
2543
2644
- name: Build package
27-
run: ./scripts/bash/package_release.sh "${{ runner.temp }}/dist"
45+
run: ./scripts/bash/package_release.sh "${{ runner.temp }}/dist" "${{ steps.version.outputs.version }}"
2846

2947
- name: Read tool versions
3048
id: tool-versions
@@ -58,6 +76,24 @@ jobs:
5876
print('All service modules imported successfully')
5977
"
6078
79+
- name: Create or Update Release
80+
if: github.ref_type == 'tag' || inputs.new_release_published
81+
env:
82+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
83+
VERSION: ${{ steps.version.outputs.version }}
84+
run: |
85+
# Create the release if it doesn't exist. If it does, ignore the error.
86+
gh release create "$VERSION" \
87+
--title "$VERSION" \
88+
--generate-notes \
89+
2>/dev/null || echo "Release already exists, uploading assets..."
90+
91+
# Upload or overwrite assets
92+
gh release upload "$VERSION" \
93+
${{ runner.temp }}/dist/gateway-*.zip \
94+
${{ runner.temp }}/dist/gateway-*.zip.sha256 \
95+
--clobber
96+
6197
- name: Upload artifact
6298
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
6399
with:

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,16 @@ Run manually:
114114
make githooks-run
115115
```
116116

117+
## Deployment
118+
119+
### Windows Service
120+
121+
The gateway is designed to run as a set of Windows Services on-premises.
122+
123+
- **Deployment Guide**: [docs/deployment/windows-service-deploy.md](docs/deployment/windows-service-deploy.md)
124+
- **Deployment Script**: `scripts/powershell/deploy.ps1`
125+
- **Strategy**: Blue/Green with automatic rollback and health checks.
126+
117127
## Architecture
118128

119129
This gateway implements a lightweight DICOM service architecture:

0 commit comments

Comments
 (0)