Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
eca0631
[GPCAPIM-254]: Lift and shift of lambda functionality in to a Flask app.
davidhamill1-nhs Jan 14, 2026
11f39d3
[GPCAPIM-254]: Lambda is no longer being used; move actions away from…
davidhamill1-nhs Jan 14, 2026
6715384
[GPCAPIM-254]: Github not picking up unindented input
davidhamill1-nhs Jan 14, 2026
5e15c15
[GPCAPIM-254]: Github not picking up unindented input
davidhamill1-nhs Jan 14, 2026
5f2e8c2
Merge remote-tracking branch 'refs/remotes/origin/feature/GPCAPIM-254…
davidhamill1-nhs Jan 14, 2026
f0be4f0
[GPCAPIM-254]: Github not picking up unindented input
davidhamill1-nhs Jan 14, 2026
7c2ca4a
[GPCAPIM-254]: Add type hinting
davidhamill1-nhs Jan 14, 2026
8eb80d0
[GPCAPIM-254]: Beginning of /patient/$gpc.getstructuredrecord endpoint.
davidhamill1-nhs Jan 15, 2026
6f72813
[GPCAPIM-254]: Handle logic in request-specific class
davidhamill1-nhs Jan 19, 2026
34ceab7
[GPCAPIM-254]: Move to handler class
davidhamill1-nhs Jan 19, 2026
1fc6528
[GPCAPIM-254]: Update healthcheck endpoint to return simplier body.
davidhamill1-nhs Jan 20, 2026
7b9e59c
[GPCAPIM-254]: Remove the lambda.
davidhamill1-nhs Jan 20, 2026
a0d594e
[GPCAPIM-254]: Clean up github actions.
davidhamill1-nhs Jan 20, 2026
a21dcee
[GPCAPIM-254]: Clean up.
davidhamill1-nhs Jan 21, 2026
3157ddb
[GPCAPIM-254]: Unit tests no loonger exist in the top level.
davidhamill1-nhs Jan 21, 2026
e882bcf
[GPCAPIM-254]: Correct content-type header.
davidhamill1-nhs Jan 21, 2026
ba7fa30
[GPCAPIM-254]: Handle response object, rather than just pass back dict.
davidhamill1-nhs Jan 21, 2026
8dfb142
[GPCAPIM-254]: Correct content-type header for healthcheck.
davidhamill1-nhs Jan 21, 2026
bceea35
[GPCAPIM-254]: Add error hanlding in app.
davidhamill1-nhs Jan 21, 2026
f1a3fad
[GPCAPIM-254]: Force new deployment of ecs task in preview environment.
davidhamill1-nhs Jan 21, 2026
d9ee75d
Revert "[GPCAPIM-254]: Force new deployment of ecs task in preview en…
davidhamill1-nhs Jan 21, 2026
d379723
[GPCAPIM-254]: Make it clear which version is deployed in the health …
davidhamill1-nhs Jan 21, 2026
12dfae8
[GPCAPIM-254]: Correct module name.
davidhamill1-nhs Jan 21, 2026
791ecc4
[GPCAPIM-254]: Use tech radars preferred alpine and run thorugh non-r…
davidhamill1-nhs Jan 21, 2026
2fca0d7
[GPCAPIM-254]: APIM handles CSRF through its auth design; We don't ha…
davidhamill1-nhs Jan 22, 2026
0bff6ce
[GPCAPIM-254]: Reduce fragility of code by pushing environment variab…
davidhamill1-nhs Jan 22, 2026
07418b9
[GPCAPIM-254]: Reduce fragility of code by ensuring headers are corre…
davidhamill1-nhs Jan 22, 2026
a844d57
[GPCAPIM-254]: Reduce fragility of code by ensuring NHS number is cor…
davidhamill1-nhs Jan 22, 2026
63a576b
[GPCAPIM-254]: CSRF alert will be disabled in SonarQube; we do not ne…
davidhamill1-nhs Jan 26, 2026
7b37a40
[GPCAPIM-254]: Mock Request.geT_json() method by return the request_b…
davidhamill1-nhs Jan 26, 2026
47c4509
[GPCAPIM-254]: Pytest, by default, runs all test_*.py files. Passing …
davidhamill1-nhs Jan 26, 2026
aa7f11a
[GPCAPIM-254]: Move print lines in to test covered functions to stop …
davidhamill1-nhs Jan 26, 2026
22f63eb
[GPCAPIM-254]: Make the behaviour of the payload more explicit.
davidhamill1-nhs Jan 26, 2026
3e3e64c
[GPCAPIM-254]: Constants should be UPPERCASE for clarity.
davidhamill1-nhs Jan 26, 2026
5832364
[GPCAPIM-254]: No longer SSP-from/SSP-to headers; moving towards ODS-…
davidhamill1-nhs Jan 26, 2026
0414821
[GPCAPIM-254]: It's a numbers game.
davidhamill1-nhs Jan 26, 2026
42720e9
[GPCAPIM-254]: Correct name of test.
davidhamill1-nhs Jan 26, 2026
f814694
[GPCAPIM-254]: Correct step name.
davidhamill1-nhs Jan 26, 2026
5bda36c
Refactor branch naming conventions for ECS resources and log groups
neil-sproston Jan 15, 2026
de46a52
Add Terraform output capture and comment on PR with preview details
neil-sproston Jan 19, 2026
a099b04
Add permissions to add PR comment
neil-sproston Jan 19, 2026
56dd949
[GPCAPIM-251] Create provider request module with lightweight stub
DWolfsNHS Jan 8, 2026
12056dd
[GPCAPIM-166] Enhance workflow of preview environment
neil-sproston Jan 23, 2026
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
50 changes: 50 additions & 0 deletions .github/actions/start-app/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: "Start local app"
description: "Start Flask app that will handle requests"
inputs:
deploy-command:
description: "Command to start app"
required: false
default: "make deploy"
health-path:
description: "Health check path"
required: false
default: "/health"
max-seconds:
description: "Maximum seconds to wait for readiness"
required: false
default: "60"
python-version:
description: "Python version to install"
required: true
runs:
using: "composite"
steps:
- name: "Start app"
shell: bash
env:
PYTHON_VERSION: ${{ inputs.python-version }}
run: |
set -euo pipefail
echo "Starting app: '${{ inputs.deploy-command }}'"
nohup ${{ inputs.deploy-command }} > /tmp/app.log 2>&1 &
echo $! > /tmp/app.pid
echo "PID: $(cat /tmp/app.pid)"
- name: "Wait for app to be ready"
shell: bash
run: |
set -euo pipefail
BASE_URL="${BASE_URL:-http://localhost:5000}"
HEALTH_URL="${BASE_URL}${{ inputs.health-path }}"
MAX="${{ inputs.max-seconds }}"
echo "Waiting for app at ${HEALTH_URL} (max ${MAX}s)..."
for i in $(seq 1 "${MAX}"); do
if curl -sSf -X GET "${HEALTH_URL}" >/dev/null; then
echo "App is ready"
exit 0
fi
sleep 1
done
echo "App did not become ready in time"
echo "---- recent app log ----"
tail -n 200 /tmp/app.log || true
exit 1
50 changes: 0 additions & 50 deletions .github/actions/start-local-lambda/action.yaml

This file was deleted.

94 changes: 90 additions & 4 deletions .github/workflows/preview-env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ env:
AWS_ACCOUNT_ID: "900119715266"
ECR_REPOSITORY_NAME: "whoami"
TF_STATE_BUCKET: "cds-cdg-dev-tfstate-900119715266"
CORE_STATE_KEY: "dev/terraform.tfstate"
PREVIEW_STATE_PREFIX: "dev/preview/"
python_version: "3.14"

Expand All @@ -22,6 +21,7 @@ jobs:
permissions:
id-token: write
contents: read
pull-requests: write

# One job per branch at a time
concurrency:
Expand Down Expand Up @@ -78,7 +78,6 @@ jobs:
echo "tf_state_key=$TF_STATE_KEY" >> $GITHUB_OUTPUT

# ALB listener rule priority - derive from PR number (must be unique per listener)
# You can tweak this formula if you like.
if [ -n "${{ github.event.number }}" ]; then
PRIORITY=$(( 1000 + ${{ github.event.number }} ))
else
Expand Down Expand Up @@ -134,10 +133,40 @@ jobs:
TF_VAR_image_tag: ${{ steps.meta.outputs.branch_name }}
TF_VAR_alb_rule_priority: ${{ steps.meta.outputs.alb_rule_priority }}
run: |
terraform apply -auto-approve
terraform apply \
-auto-approve

# ---------- DESTROY (PR closed) ----------
- name: Capture preview TF outputs
if: github.event.action != 'closed'
id: tf-output
working-directory: infrastructure/environments/preview
run: |
terraform output -json > tf-output.json

URL=$(jq -r '.url.value' tf-output.json)
echo "preview_url=$URL" >> $GITHUB_OUTPUT

TG=$(jq -r '.target_group_arn.value' tf-output.json)
echo "target_group=$TG" >> $GITHUB_OUTPUT

ECS_SERVICE=$(jq -r '.ecs_service_name.value' tf-output.json)
echo "ecs_service=$ECS_SERVICE" >> $GITHUB_OUTPUT

ECS_CLUSTER=$(jq -r '.ecs_cluster_name.value' tf-output.json)
echo "ecs_cluster=$ECS_CLUSTER" >> $GITHUB_OUTPUT

# ---------- Ensure re-deployment (PR updated) ----------
- name: Force ECS service redeployment
if: github.event.action == 'synchronize'
id: await-redeployment
run: |
aws ecs update-service \
--cluster ${{ steps.tf-output.outputs.ecs_cluster }} \
--service ${{ steps.tf-output.outputs.ecs_service }} \
--force-new-deployment \
--region ${{ env.AWS_REGION }}

# ---------- DESTROY (PR closed) ----------
- name: Terraform init (destroy)
if: github.event.action == 'closed'
working-directory: infrastructure/environments/preview
Expand All @@ -156,3 +185,60 @@ jobs:
TF_VAR_alb_rule_priority: ${{ steps.meta.outputs.alb_rule_priority }}
run: |
terraform destroy -auto-approve

# ---------- Wait on AWS tasks and notify ----------
- name: Await deployment completion
if: github.event.action != 'closed'
run: |
aws ecs wait services-stable \
--cluster ${{ steps.tf-output.outputs.ecs_cluster }} \
--services ${{ steps.tf-output.outputs.ecs_service }} \
--region ${{ env.AWS_REGION }}

- name: Comment function name on PR
if: github.event_name == 'pull_request' && github.event.action != 'closed'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
with:
script: |
const alb = '${{ steps.tf-output.outputs.target_group }}';
const url = '${{ steps.tf-output.outputs.preview_url }}';
const cluster = '${{ steps.tf-output.outputs.ecs_cluster }}';
const service = '${{ steps.tf-output.outputs.ecs_service }}';
const owner = context.repo.owner;
const repo = context.repo.repo;
const issueNumber = context.issue.number;

const { data: comments } = await github.rest.issues.listComments({
owner,
repo,
issue_number: issueNumber,
per_page: 100,
});

for (const comment of comments) {
const isBot = comment.user?.login === 'github-actions[bot]';
const isPreviewUpdate = comment.body?.includes('Deployment Complete');

if (isBot && isPreviewUpdate) {
await github.rest.issues.deleteComment({
owner,
repo,
comment_id: comment.id,
});
}
}

const lines = [
'**Deployment Complete**',
`- Preview URL: [${url}](${url})`,
`- ECS Cluster: \`${cluster}\``,
`- ECS Service: \`${service}\``,
`- ALB Target: \`${alb}\``,
];

await github.rest.issues.createComment({
owner,
repo,
issue_number: issueNumber,
body: lines.join('\n'),
});
16 changes: 8 additions & 8 deletions .github/workflows/stage-2-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ jobs:
uses: ./.github/actions/setup-python-project
with:
python-version: ${{ inputs.python_version }}
- name: "Start local Lambda"
uses: ./.github/actions/start-local-lambda
- name: "Start app"
uses: ./.github/actions/start-app
with:
python-version: ${{ inputs.python_version }}
- name: "Run contract tests"
Expand Down Expand Up @@ -98,8 +98,8 @@ jobs:
uses: ./.github/actions/setup-python-project
with:
python-version: ${{ inputs.python_version }}
- name: "Start local Lambda"
uses: ./.github/actions/start-local-lambda
- name: "Start app"
uses: ./.github/actions/start-app
with:
python-version: ${{ inputs.python_version }}
- name: "Run schema validation tests"
Expand Down Expand Up @@ -128,8 +128,8 @@ jobs:
uses: ./.github/actions/setup-python-project
with:
python-version: ${{ inputs.python_version }}
- name: "Start local Lambda"
uses: ./.github/actions/start-local-lambda
- name: "Start app"
uses: ./.github/actions/start-app
with:
python-version: ${{ inputs.python_version }}
- name: "Run integration test"
Expand Down Expand Up @@ -158,8 +158,8 @@ jobs:
uses: ./.github/actions/setup-python-project
with:
python-version: ${{ inputs.python_version }}
- name: "Start local Lambda"
uses: ./.github/actions/start-local-lambda
- name: "Start app"
uses: ./.github/actions/start-app
with:
python-version: ${{ inputs.python_version }}
max-seconds: 90
Expand Down
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ IMAGE_REPOSITORY := ${ECR_URL}
endif

IMAGE_NAME := ${IMAGE_REPOSITORY}:${IMAGE_TAG}
COMMIT_VERSION := $(shell git rev-parse --short HEAD)
BUILD_DATE := $(shell date -u +"%Y%m%d")
# ==============================================================================

# Example CI/CD targets are: dependencies, build, publish, deploy, clean, etc.
Expand All @@ -34,9 +36,8 @@ build-gateway-api: dependencies
@poetry run mypy --no-namespace-packages .
@echo "Packaging dependencies..."
@poetry build --format=wheel
@pip install "dist/gateway_api-0.1.0-py3-none-any.whl" --target "./target/gateway-api" --platform musllinux_1_1_x86_64 --only-binary=:all:
@pip install "dist/gateway_api-0.1.0-py3-none-any.whl" --target "./target/gateway-api" --platform musllinux_1_2_x86_64 --only-binary=:all:
# Copy main file separately as it is not included within the package.
@cp lambda_handler.py ./target/gateway-api/
@rm -rf ../infrastructure/images/gateway-api/resources/build/
@mkdir ../infrastructure/images/gateway-api/resources/build/
@cp -r ./target/gateway-api ../infrastructure/images/gateway-api/resources/build/
Expand All @@ -46,7 +47,7 @@ build-gateway-api: dependencies
.PHONY: build
build: build-gateway-api # Build the project artefact @Pipeline
@echo "Building Docker x86 image using Docker. Utilising python version: ${PYTHON_VERSION} ..."
@$(docker) buildx build --platform linux/amd64 --load --provenance=false --build-arg PYTHON_VERSION=${PYTHON_VERSION} -t ${IMAGE_NAME} infrastructure/images/gateway-api
@$(docker) buildx build --platform linux/amd64 --load --provenance=false --build-arg PYTHON_VERSION=${PYTHON_VERSION} --build-arg COMMIT_VERSION=${COMMIT_VERSION} --build-arg BUILD_DATE=${BUILD_DATE} -t ${IMAGE_NAME} infrastructure/images/gateway-api
@echo "Docker image '${IMAGE_NAME}' built successfully!"

publish: # Publish the project artefact @Pipeline
Expand Down
38 changes: 0 additions & 38 deletions gateway-api/lambda_handler.py

This file was deleted.

Loading