Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/actions/proxy/deploy-proxy/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ inputs:
proxygen-environment:
description: 'Proxygen environment'
required: true
pr-number:
description: 'Pull request number. When provided, appends "(PR-<number>)" to the API title in the proxy specification to distinguish PR-based proxies in the developer portal.'
required: false
default: ""

runs:
using: composite
Expand All @@ -48,6 +52,14 @@ runs:

yq eval '.x-nhsd-apim.target.url = env(TARGET_URL) | .x-nhsd-apim.target.security.secret = env(MTLS_SECRET_NAME)' -i /tmp/proxy-specification.yaml

- name: Append PR number to API title
if: ${{ inputs.pr-number != '' }}
env:
PR_NUMBER: "${{ inputs.pr-number }}"
shell: bash
run: |
yq eval '.info.title = .info.title + " (PR-" + env(PR_NUMBER) + ")"' -i /tmp/proxy-specification.yaml

- name: Deploy API proxy
env:
ENVIRONMENT: "${{ inputs.proxygen-environment }}"
Expand Down
40 changes: 40 additions & 0 deletions .github/actions/proxy/publish-proxy/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Publish API Proxy Specification
description: Publish the Specification to the relevant catalogue

inputs:
proxygen-key-secret:
description: 'Proxygen private key secret'
required: true
proxygen-key-id:
description: 'Proxygen key ID'
required: true
proxygen-client-id:
description: 'Proxygen client ID'
required: true
proxygen-api-name:
description: 'Proxygen API name'
required: true
catalogue:
description: 'Catalogue to which the specification will be published'
required: false
default: "uat"

runs:
using: composite
steps:
- name: Configure Proxygen
uses: ./.github/actions/proxy/configure-proxygen
with:
proxygen-key-secret: ${{ inputs.proxygen-key-secret }}
proxygen-key-id: ${{ inputs.proxygen-key-id }}
proxygen-client-id: ${{ inputs.proxygen-client-id }}
proxygen-api-name: ${{ inputs.proxygen-api-name }}

- name: Publish specification
env:
CATALOGUE: ${{ inputs.catalogue }}
shell: bash
run: |
FLAGS="--no-confirm"
[[ "$CATALOGUE" != "prod" ]] && FLAGS="--uat $FLAGS"
proxygen spec publish pathology-api/openapi.yaml $FLAGS
151 changes: 83 additions & 68 deletions .github/workflows/integration-env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,30 @@ name: Integration Environment

on:
pull_request:
branches: [integration]
types: [closed]
branches: [ integration ]
types: [ closed ]
workflow_dispatch:
inputs:
create-release:
description: "Create release"
required: true
default: false
type: boolean

env:
AWS_REGION: eu-west-2
PREVIEW_PREFIX: int-
PYTHON_VERSION: 3.14
LAMBDA_RUNTIME: python3.14
LAMBDA_HANDLER: lambda_handler.handler
MTLS_SECRET_NAME: ${{ vars.PREVIEW_ENV_MTLS_SECRET_NAME }}
MTLS_SECRET_NAME: ${{ vars.INT_MTLS_SECRET_NAME }}
PROXYGEN_KEY_ID: ${{ vars.PREVIEW_ENV_PROXYGEN_KEY_ID }}
PROXYGEN_CLIENT_ID: ${{ vars.PREVIEW_ENV_PROXYGEN_CLIENT_ID }}
PROXYGEN_API_NAME: ${{ vars.PROXYGEN_API_NAME }}
BASE_URL: "https://internal-dev.api.service.nhs.uk/${{ vars.PROXYGEN_API_NAME }}-integration"
BASE_URL: "https://int.api.service.nhs.uk/pathology-laboratory-reporting"
ENV: "remote"
HOST: "internal-dev.api.service.nhs.uk"
HOST: "int.api.service.nhs.uk"
PROXY_ENV: "int"

jobs:
integration-environment:
Expand Down Expand Up @@ -68,7 +75,7 @@ jobs:
echo "lambda_role=$LAMBDA_ROLE_ARN" >> "$GITHUB_OUTPUT"

- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@5e19c1aa7acd2e02c5110eaf77eacd29039cca28
uses: aws-actions/configure-aws-credentials@bc9489585819302995bb108bbd899b7975f40303
with:
role-to-assume: ${{ steps.role-select.outputs.aws_role }}
aws-region: ${{ env.AWS_REGION }}
Expand Down Expand Up @@ -103,7 +110,7 @@ jobs:
echo "int_url=$URL" >> "$GITHUB_OUTPUT"

# ---------- Handle application with int ----------
- name: Create or update preview Lambda with int
- name: Create or update preview Lambda in int
env:
MOCK_URL: ${{ steps.names.outputs.int_url }}
TOKEN_EXPIRY_THRESHOLD: ${{ secrets.APIM_TOKEN_EXPIRY_THRESHOLD }}
Expand All @@ -114,6 +121,9 @@ jobs:
API_MTLS_KEY: ${{ secrets.API_MTLS_KEY }}
APIM_KEY_ID: ${{ secrets.APIM_KEY_ID }}
CLIENT_REQUEST_TIMEOUT: ${{ secrets.CLIENT_REQUEST_TIMEOUT }}
APIM_TOKEN_URL: ${{ vars.INT_APIM_TOKEN_URL }}
MNS_EVENT_URL: ${{ vars.INT_MNS_EVENT_URL }}
PDM_BUNDLE_URL: ${{ vars.INT_PDM_BUNDLE_URL }}
run: |
cd pathology-api/target/
FN="${{ steps.names.outputs.function_name }}"
Expand All @@ -123,7 +133,10 @@ jobs:
API_KEY="${APIM_APIKEY:-/cds/pathology/int/apim/api-key}"
MTLS_CERT="${API_MTLS_CERT:-/cds/pathology/int/mtls/client1-key-public}"
MTLS_KEY="${API_MTLS_KEY:-/cds/pathology/int/mtls/client1-key-secret}"
KEY_ID="${APIM_KEY_ID:-DEV-1}"
KEY_ID="${APIM_KEY_ID:-INT-1}"
APIM_TOKEN_URL="${APIM_TOKEN_URL}"
MNS_EVENT_URL="${MNS_EVENT_URL}"
PDM_BUNDLE_URL="${PDM_BUNDLE_URL}"
CLIENT_TIMEOUT="${CLIENT_REQUEST_TIMEOUT:-10s}"
echo "Deploying preview function: $FN"
wait_for_lambda_ready() {
Expand Down Expand Up @@ -154,9 +167,9 @@ jobs:
APIM_MTLS_CERT_NAME=$MTLS_CERT, \
APIM_MTLS_KEY_NAME=$MTLS_KEY, \
APIM_KEY_ID=$KEY_ID, \
APIM_TOKEN_URL=$MOCK_URL/apim/oauth2/token, \
PDM_BUNDLE_URL=$MOCK_URL/apim/check_auth, \
MNS_EVENT_URL=$MOCK_URL/mns, \
APIM_TOKEN_URL=$APIM_TOKEN_URL, \
MNS_EVENT_URL=$MNS_EVENT_URL, \
PDM_BUNDLE_URL=$PDM_BUNDLE_URL, \
CLIENT_TIMEOUT=$CLIENT_TIMEOUT, \
JWKS_SECRET_NAME=$JWKS_SECRET}" || true
wait_for_lambda_ready
Expand All @@ -177,16 +190,16 @@ jobs:
APIM_KEY_ID=$KEY_ID, \
APIM_MTLS_CERT_NAME=$MTLS_CERT, \
APIM_MTLS_KEY_NAME=$MTLS_KEY, \
APIM_TOKEN_URL=$MOCK_URL/apim/oauth2/token, \
PDM_BUNDLE_URL=$MOCK_URL/apim/check_auth, \
MNS_EVENT_URL=$MOCK_URL/mns, \
APIM_TOKEN_URL=$APIM_TOKEN_URL, \
MNS_EVENT_URL=$MNS_EVENT_URL, \
PDM_BUNDLE_URL=$PDM_BUNDLE_URL, \
CLIENT_TIMEOUT=$CLIENT_TIMEOUT, \
JWKS_SECRET_NAME=$JWKS_SECRET}" \
--publish
wait_for_lambda_ready
fi

- name: Output function name with mock
- name: Output function name for int
run: |
echo "function = ${{ steps.names.outputs.function_name }}"
echo "url = ${{ steps.names.outputs.int_url }}"
Expand Down Expand Up @@ -258,16 +271,17 @@ jobs:
secret-ids: /cds/pathology/int/proxygen/proxygen-key-secret
name-transformation: lowercase

- name: Deploy preview API proxy
- name: Deploy integration API proxy
uses: ./.github/actions/proxy/deploy-proxy
with:
mtls-secret-name: ${{ env.MTLS_SECRET_NAME }}
target-url: ${{ steps.names.outputs.int_url }}
proxy-base-path: "${{ env.PROXYGEN_API_NAME }}-integration"
proxy-base-path: ${{ env.PROXYGEN_API_NAME }}
proxygen-key-secret: ${{ env._cds_pathology_int_proxygen_proxygen_key_secret }}
proxygen-key-id: ${{ env.PROXYGEN_KEY_ID }}
proxygen-client-id: ${{ env.PROXYGEN_CLIENT_ID }}
proxygen-api-name: ${{ env.PROXYGEN_API_NAME }}
proxygen-environment: ${{ env.PROXY_ENV }}

- name: Retrieve Apigee Token
id: apigee-token
Expand All @@ -293,87 +307,88 @@ jobs:

# ---------- Test suites ----------
- name: "Run unit tests"
continue-on-error: true
uses: ./.github/actions/run-test-suite
with:
test-type: unit
env: local

- name: "Run contract tests"
continue-on-error: true
uses: ./.github/actions/run-test-suite
with:
test-type: contract
apigee-access-token: ${{ steps.apigee-token.outputs.apigee-access-token }}

- name: "Run schema validation tests"
continue-on-error: true
uses: ./.github/actions/run-test-suite
with:
test-type: schema
apigee-access-token: ${{ steps.apigee-token.outputs.apigee-access-token }}

- name: "Run integration tests"
continue-on-error: true
uses: ./.github/actions/run-test-suite
with:
test-type: integration
apigee-access-token: ${{ steps.apigee-token.outputs.apigee-access-token }}

- name: "Run acceptance tests"
continue-on-error: true
uses: ./.github/actions/run-test-suite
with:
test-type: acceptance
apigee-access-token: ${{ steps.apigee-token.outputs.apigee-access-token }}
# ---------- Perform vuln scan and notify ----------
# - name: Filesystem vuln scan
# - name: SBOM generation

# ---------- Coverage & reporting ----------
- name: "Download all test coverage artefacts"
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: pathology-api/test-artefacts/
merge-multiple: false
- name: "Download mock test coverage artefacts"
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: mocks/test-artefacts/
merge-multiple: false
- name: "Merge coverage data"
run: make test-coverage
- name: "Rename coverage XML with unique name"
run: |
cd pathology-api/test-artefacts
mv coverage-merged.xml "${{ steps.create-name.outputs.artefact-name }}.xml"
cd ../..
cd mocks/test-artefacts
mv coverage-merged.xml ${{ steps.create-name.outputs.artefact-name }}-mocks.xml
- name: "Upload combined coverage report"
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ steps.create-name.outputs.artefact-name }}
path: pathology-api/test-artefacts
retention-days: 30
- name: "Upload mocks coverage report"
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ steps.create-name.outputs.artefact-name }}-mocks
path: mocks/test-artefacts
retention-days: 30
- name: "Download merged coverage report"
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
release-build:
name: "Build release on GIT"
runs-on: ubuntu-latest
needs: integration-environment
if: >
(github.event_name == 'pull_request' && github.event.pull_request.merged) || (github.event_name == 'workflow_dispatch' && inputs.create-release)

steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
name: ${{ steps.create-name.outputs.artefact-name }}
path: coverage-reports/
- name: "Download mock coverage report"
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
fetch-depth: 0
ref: integration

- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
name: ${{ steps.create-name.outputs.artefact-name }}-mocks
path: coverage-reports/
- name: "SonarCloud Scan"
uses: SonarSource/sonarqube-scan-action@299e4b793aaa83bf2aba7c9c14bedbb485688ec4 #7.1.0
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
python-version: "${{ env.PYTHON_VERSION }}"

- name: Setup Python project
uses: ./.github/actions/setup-python-project
with:
args: >
-Dsonar.organization=${{ vars.SONAR_ORGANISATION_KEY }}
-Dsonar.projectKey=${{ vars.SONAR_PROJECT_KEY }}
-Dsonar.python.coverage.reportPaths=coverage-reports/${{ steps.create-name.outputs.artefact-name }}.xml,coverage-reports/${{ steps.create-name.outputs.artefact-name }}-mocks.xml
python-version: ${{ env.PYTHON_VERSION }}

# ---------- Perform vuln scan and notify ----------
# - name: Filesystem vuln scan
# - name: SBOM generation
- name: Package artifacts
run: |
make build

- name: Compute tag
id: version
run: |
BASE_VERSION="$(poetry --directory pathology-api version --short)"
TAG="v${BASE_VERSION}-${GITHUB_RUN_NUMBER}"
echo "Computed tag: $TAG"
echo "base_version=$BASE_VERSION" >> "$GITHUB_OUTPUT"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"

- name: Create GitHub release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "${{ steps.version.outputs.tag }}" \
--title "Release ${{ steps.version.outputs.tag }}" \
--target integration \
--draft \
--generate-notes \
--fail-on-no-commits \
pathology-api/target/artifact.zip || true
6 changes: 4 additions & 2 deletions .github/workflows/preview-env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
pr-preview:
name: "PR preview management"
runs-on: ubuntu-latest
if: github.head_ref != 'main' && github.head_ref != 'integration'
outputs:
function_name: ${{ steps.names.outputs.function_name }}
preview_url: ${{ steps.names.outputs.preview_url }}
Expand Down Expand Up @@ -79,7 +80,7 @@ jobs:
fi

- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@5e19c1aa7acd2e02c5110eaf77eacd29039cca28
uses: aws-actions/configure-aws-credentials@bc9489585819302995bb108bbd899b7975f40303
with:
role-to-assume: ${{ steps.role-select.outputs.aws_role }}
aws-region: ${{ env.AWS_REGION }}
Expand Down Expand Up @@ -594,6 +595,7 @@ jobs:
proxygen-client-id: ${{ env.PROXYGEN_CLIENT_ID }}
proxygen-api-name: ${{ env.PROXYGEN_API_NAME }}
proxygen-environment: ${{ env.PROXY_ENV}}
pr-number: ${{ github.event.pull_request.number }}

- name: Deploy int preview API proxy
if: github.event.action != 'closed' && github.event.pull_request.user.login != 'dependabot[bot]'
Expand All @@ -607,6 +609,7 @@ jobs:
proxygen-client-id: ${{ env.PROXYGEN_CLIENT_ID }}
proxygen-api-name: ${{ env.PROXYGEN_API_NAME }}
proxygen-environment: ${{ env.PROXY_ENV}}
pr-number: "${{ github.event.pull_request.number }}i"

- name: Tear down preview API proxy
if: github.event.action == 'closed'
Expand Down Expand Up @@ -839,7 +842,6 @@ jobs:
issue_number: issueNumber,
body: lines.join('\n'),
});

# ---------- Perform vuln scan and notify ----------
# - name: Filesystem vuln scan
# - name: SBOM generation
Loading
Loading