Skip to content

Commit cf14cc5

Browse files
authored
Merge branch 'main' into copilot
2 parents 954ad15 + f2d4d69 commit cf14cc5

24 files changed

Lines changed: 2863 additions & 1504 deletions

.devcontainer/devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"args": {
77
"DOCKER_GID": "${env:DOCKER_GID:}",
88
"IMAGE_NAME": "node_24_python_3_14",
9-
"IMAGE_VERSION": "v1.0.6",
9+
"IMAGE_VERSION": "v1.2.0",
1010
"USER_UID": "${localEnv:USER_ID:}",
1111
"USER_GID": "${localEnv:GROUP_ID:}"
1212
},

.gitallowed

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ self\.token = token
77
token = os\.environ\.get\(\"GH_TOKEN\"\)
88
poetry\.lock
99
\-Dsonar\.token=\"\$SONAR_TOKEN\"
10+
token: "\${{ steps\.generate-token\.outputs\.token }}"
11+
id-token: 'write'
12+
id-token: "write"

.github/dependabot.yaml

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,37 @@ updates:
1212
open-pull-requests-limit: 20
1313
commit-message:
1414
prefix: "Upgrade: [dependabot] - "
15+
cooldown:
16+
default-days: 3
1517

1618
###################################
17-
# NPM workspace ##################
19+
# Poetry #########################
1820
###################################
19-
- package-ecosystem: "npm"
21+
- package-ecosystem: "pip"
2022
directory: "/"
2123
schedule:
2224
interval: "weekly"
2325
day: "thursday"
24-
time: "18:00" # UTC
26+
time: "20:00" # UTC
2527
open-pull-requests-limit: 20
2628
versioning-strategy: increase
2729
commit-message:
2830
prefix: "Upgrade: [dependabot] - "
31+
cooldown:
32+
default-days: 3
2933

3034
###################################
31-
# Poetry #########################
35+
# NPM workspace ##################
3236
###################################
33-
- package-ecosystem: "pip"
37+
- package-ecosystem: "npm"
3438
directory: "/"
3539
schedule:
3640
interval: "weekly"
3741
day: "thursday"
38-
time: "18:00" # UTC
42+
time: "22:00" # UTC
3943
open-pull-requests-limit: 20
4044
versioning-strategy: increase
4145
commit-message:
4246
prefix: "Upgrade: [dependabot] - "
47+
cooldown:
48+
default-days: 3

.github/workflows/dependabot-auto-approve-and-merge.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
steps:
2020
- name: Get token from Github App
2121
id: get_app_token
22-
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf
22+
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859
2323
with:
2424
app-id: ${{ secrets.AUTOMERGE_APP_ID }}
2525
private-key: ${{ secrets.AUTOMERGE_PEM }}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
name: Get Repo Config and Image
2+
on:
3+
workflow_call:
4+
inputs:
5+
registry:
6+
required: false
7+
type: string
8+
default: ghcr.io
9+
namespace:
10+
required: false
11+
type: string
12+
default: nhsdigital/eps-devcontainers
13+
owner:
14+
required: false
15+
type: string
16+
default: NHSDigital
17+
verify_published_from_main_image:
18+
required: false
19+
type: boolean
20+
default: true
21+
predicate_type:
22+
required: false
23+
type: string
24+
default: https://slsa.dev/provenance/v1
25+
outputs:
26+
tag_format:
27+
description: The tag format to be used for releases, as defined in .github/config/settings.yml
28+
value: ${{ jobs.get_config_values.outputs.tag_format }}
29+
devcontainer_image:
30+
description: The devcontainer image name as defined in .devcontainer/devcontainer.json
31+
value: ${{ jobs.get_config_values.outputs.devcontainer_image }}
32+
devcontainer_version:
33+
description: The devcontainer image version as defined in .devcontainer/devcontainer.json
34+
value: ${{ jobs.get_config_values.outputs.devcontainer_version }}
35+
pinned_image:
36+
description: Fully-qualified digest-pinned image reference
37+
value: ${{ jobs.verify_attestation.outputs.pinned_image }}
38+
resolved_digest:
39+
description: Resolved digest for the supplied image reference
40+
value: ${{ jobs.verify_attestation.outputs.resolved_digest }}
41+
42+
jobs:
43+
get_config_values:
44+
runs-on: ubuntu-22.04
45+
outputs:
46+
tag_format: ${{ steps.load-config.outputs.TAG_FORMAT }}
47+
devcontainer_version: ${{ steps.load-config.outputs.DEVCONTAINER_VERSION }}
48+
devcontainer_image: ${{ steps.load-config.outputs.DEVCONTAINER_IMAGE }}
49+
runtime_docker_image: ${{ steps.load-config.outputs.RUNTIME_DOCKER_IMAGE }}
50+
steps:
51+
- name: Checkout code
52+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
53+
with:
54+
ref: ${{ env.BRANCH_NAME }}
55+
fetch-depth: 0
56+
57+
- name: Load config value
58+
id: load-config
59+
run: |
60+
TAG_FORMAT=$(yq '.TAG_FORMAT' .github/config/settings.yml)
61+
DEVCONTAINER_IMAGE=$(jq -r '.build.args.IMAGE_NAME' .devcontainer/devcontainer.json)
62+
DEVCONTAINER_VERSION=$(jq -r '.build.args.IMAGE_VERSION' .devcontainer/devcontainer.json)
63+
RUNTIME_DOCKER_IMAGE="${DEVCONTAINER_IMAGE}:githubactions-${DEVCONTAINER_VERSION}"
64+
{
65+
echo "TAG_FORMAT=$TAG_FORMAT"
66+
echo "DEVCONTAINER_IMAGE=$DEVCONTAINER_IMAGE"
67+
echo "DEVCONTAINER_VERSION=$DEVCONTAINER_VERSION"
68+
echo "RUNTIME_DOCKER_IMAGE=$RUNTIME_DOCKER_IMAGE"
69+
} >> "$GITHUB_OUTPUT"
70+
71+
verify_attestation:
72+
runs-on: ubuntu-22.04
73+
needs: get_config_values
74+
permissions:
75+
contents: read
76+
packages: read
77+
attestations: read
78+
outputs:
79+
pinned_image: ${{ steps.resolve.outputs.pinned_image }}
80+
resolved_digest: ${{ steps.resolve.outputs.resolved_digest }}
81+
steps:
82+
- name: Login to github container registry
83+
if: startsWith(inputs.registry, 'ghcr.io')
84+
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2
85+
with:
86+
registry: ghcr.io
87+
username: ${{ github.actor }}
88+
password: ${{ secrets.GITHUB_TOKEN }}
89+
90+
- name: Resolve digest
91+
id: resolve
92+
shell: bash
93+
env:
94+
RUNTIME_DOCKER_IMAGE: ${{ needs.get_config_values.outputs.runtime_docker_image }}
95+
REGISTRY: ${{ inputs.registry }}
96+
NAMESPACE: ${{ inputs.namespace }}
97+
run: |
98+
set -euo pipefail
99+
100+
if [[ "$RUNTIME_DOCKER_IMAGE" == *"/"* ]]; then
101+
IMAGE_REF="$RUNTIME_DOCKER_IMAGE"
102+
else
103+
IMAGE_REF="${REGISTRY}/${NAMESPACE}/${RUNTIME_DOCKER_IMAGE}"
104+
fi
105+
106+
if [[ "$IMAGE_REF" == *@sha256:* ]]; then
107+
IMAGE_BASE="${IMAGE_REF%@*}"
108+
RESOLVED_DIGEST="${IMAGE_REF#*@}"
109+
else
110+
RESOLVED_DIGEST=""
111+
MAX_ATTEMPTS=5
112+
for attempt in $(seq 1 "$MAX_ATTEMPTS"); do
113+
if OUTPUT="$(docker buildx imagetools inspect "$IMAGE_REF" 2>&1)"; then
114+
RESOLVED_DIGEST="$(awk '/^Digest:/ {print $2; exit}' <<< "$OUTPUT")"
115+
if [[ -n "$RESOLVED_DIGEST" ]]; then
116+
break
117+
fi
118+
echo "Digest not found in imagetools output (attempt ${attempt}/${MAX_ATTEMPTS})." >&2
119+
else
120+
echo "Failed to inspect image ${IMAGE_REF} (attempt ${attempt}/${MAX_ATTEMPTS})." >&2
121+
echo "$OUTPUT" >&2
122+
fi
123+
124+
if [[ "$attempt" -lt "$MAX_ATTEMPTS" ]]; then
125+
sleep $((attempt * 2))
126+
fi
127+
done
128+
IMAGE_BASE="${IMAGE_REF%:*}"
129+
fi
130+
131+
if [[ -z "$RESOLVED_DIGEST" ]]; then
132+
echo "Could not resolve digest for image: $IMAGE_REF" >&2
133+
exit 1
134+
fi
135+
136+
PINNED_IMAGE="${IMAGE_BASE}@${RESOLVED_DIGEST}"
137+
echo "resolved_digest=${RESOLVED_DIGEST}" >> "$GITHUB_OUTPUT"
138+
echo "pinned_image=${PINNED_IMAGE}" >> "$GITHUB_OUTPUT"
139+
echo "Resolved image reference: ${IMAGE_REF}"
140+
echo "Resolved digest: ${RESOLVED_DIGEST}"
141+
echo "Resolved image reference: ${PINNED_IMAGE}"
142+
143+
- name: Verify attestation
144+
shell: bash
145+
env:
146+
GH_TOKEN: ${{ github.token }}
147+
OWNER: ${{ inputs.owner }}
148+
VERIFY_PUBLISHED_FROM_MAIN_IMAGE: ${{ inputs.verify_published_from_main_image }}
149+
PREDICATE_TYPE: ${{ inputs.predicate_type }}
150+
PINNED_IMAGE: ${{ steps.resolve.outputs.pinned_image }}
151+
run: |
152+
set -euo pipefail
153+
154+
args=("oci://${PINNED_IMAGE}" "--owner" "$OWNER" "--predicate-type" "$PREDICATE_TYPE")
155+
156+
if [[ "$VERIFY_PUBLISHED_FROM_MAIN_IMAGE" == "true" ]]; then
157+
args+=("--source-ref" "refs/heads/main")
158+
fi
159+
160+
161+
GH_FORCE_TTY=120 gh attestation verify "${args[@]}" 2>&1
162+
echo "Verified attestation for ${PINNED_IMAGE}"

.github/workflows/pull_request.yml

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: pr
1+
name: Pull Request
22

33
on:
44
pull_request:
@@ -14,47 +14,33 @@ jobs:
1414
secrets:
1515
AUTOMERGE_APP_ID: ${{ secrets.AUTOMERGE_APP_ID }}
1616
AUTOMERGE_PEM: ${{ secrets.AUTOMERGE_PEM }}
17+
1718
pr_title_format_check:
1819
uses: ./.github/workflows/pr_title_check.yml
20+
1921
get_config_values:
20-
runs-on: ubuntu-22.04
21-
outputs:
22-
tag_format: ${{ steps.load-config.outputs.TAG_FORMAT }}
23-
devcontainer_version: ${{ steps.load-config.outputs.DEVCONTAINER_VERSION }}
24-
devcontainer_image: ${{ steps.load-config.outputs.DEVCONTAINER_IMAGE }}
25-
steps:
26-
- name: Checkout code
27-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
22+
uses: ./.github/workflows/get-repo-config.yml
23+
with:
24+
verify_published_from_main_image: false
2825

29-
- name: Load config value
30-
id: load-config
31-
run: |
32-
TAG_FORMAT=$(yq '.TAG_FORMAT' .github/config/settings.yml)
33-
DEVCONTAINER_IMAGE=$(jq -r '.build.args.IMAGE_NAME' .devcontainer/devcontainer.json)
34-
DEVCONTAINER_VERSION=$(jq -r '.build.args.IMAGE_VERSION' .devcontainer/devcontainer.json)
35-
{
36-
echo "TAG_FORMAT=$TAG_FORMAT"
37-
echo "DEVCONTAINER_IMAGE=$DEVCONTAINER_IMAGE"
38-
echo "DEVCONTAINER_VERSION=$DEVCONTAINER_VERSION"
39-
} >> "$GITHUB_OUTPUT"
4026
quality_checks:
4127
uses: ./.github/workflows/quality-checks-devcontainer.yml
4228
needs: [get_config_values]
4329
with:
44-
runtime_docker_image: "${{ needs.get_config_values.outputs.devcontainer_image }}:githubactions-${{ needs.get_config_values.outputs.devcontainer_version }}"
30+
pinned_image: ${{ needs.get_config_values.outputs.pinned_image }}
4531
secrets:
4632
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
33+
4734
tag_release:
48-
needs: [quality_checks, get_config_values]
35+
needs: get_config_values
4936
uses: ./.github/workflows/tag-release-devcontainer.yml
5037
permissions:
51-
contents: read
5238
packages: read
53-
attestations: read
39+
id-token: write
40+
contents: write
5441
with:
5542
dry_run: true
56-
runtime_docker_image: "${{ needs.get_config_values.outputs.devcontainer_image }}:githubactions-${{ needs.get_config_values.outputs.devcontainer_version }}"
43+
pinned_image: ${{ needs.get_config_values.outputs.pinned_image }}
5744
branch_name: ${{ github.event.pull_request.head.ref }}
5845
tag_format: ${{ needs.get_config_values.outputs.tag_format }}
59-
verify_published_from_main_image: false
6046
secrets: inherit

0 commit comments

Comments
 (0)