Skip to content

Commit 4b17ad8

Browse files
committed
merge
2 parents 9c326d7 + f0930d0 commit 4b17ad8

546 files changed

Lines changed: 50802 additions & 17969 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.devcontainer/Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
FROM mcr.microsoft.com/vscode/devcontainers/python:3.12
1+
FROM mcr.microsoft.com/vscode/devcontainers/python:3.12-bookworm
2+
3+
RUN rm -f /etc/apt/sources.list.d/yarn.list
24

35
ENV POETRY_VERSION="1.7.1"
46

.devcontainer/devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@
134134
},
135135
"features": {
136136
"ghcr.io/devcontainers/features/node:1": {
137-
"version": "16.20.0"
137+
"version": "22.23.1"
138138
}
139139
},
140140
"postCreateCommand": "/usr/local/bin/notify-dev-entrypoint.sh",

.devcontainer/scripts/notify-dev-entrypoint.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,14 @@ set -ex
1111
git config --global --add safe.directory /workspaces/notification-admin
1212
git config --global --add safe.directory /workspace
1313

14+
# Configure SSH commit signing using the forwarded SSH agent key
15+
if ssh-add -L &>/dev/null; then
16+
SSH_PUB_KEY=$(ssh-add -L | head -n 1)
17+
git config --global gpg.format ssh
18+
git config --global user.signingkey "key::${SSH_PUB_KEY}"
19+
git config --global commit.gpgsign true
20+
fi
21+
22+
1423
# Install and setup dev environment
1524
installations.sh

.git-blame-ignore-revs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@
88
# Migration to Ruff: https://github.com/cds-snc/notification-admin/commit/1a94276ddde1d4c1ad2d62698d781ea14a1daaa5
99
1a94276ddde1d4c1ad2d62698d781ea14a1daaa5
1010
# Ruff formatting: https://github.com/cds-snc/notification-admin/commit/c36701742e65a9bf99e93ddfcfcbf4e38335a573
11-
c36701742e65a9bf99e93ddfcfcbf4e38335a573
11+
c36701742e65a9bf99e93ddfcfcbf4e38335a573
12+
# Reformat for ruff update https://github.com/cds-snc/notification-admin/commit/8d0da9873b86b54fcc4497aaa80c1d05888414ad
13+
8d0da9873b86b54fcc4497aaa80c1d05888414ad

.github/agents/admin-api-client-builder.md

Lines changed: 715 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
description: "Use when: scaffolding a full-stack Notify feature end-to-end across both the API and Admin repos. Chains the REST Endpoint Builder (API repo) and Admin API Client Builder (Admin repo) in sequence."
3+
tools: [read, edit, search, todo, agent]
4+
agents: [rest-endpoint-builder, admin-api-client-builder]
5+
argument-hint: "Describe the resource, its fields, and what CRUD operations are needed (e.g., 'CRUD for Feedback with service_id, rating, comment')"
6+
---
7+
8+
You are the **Notify Full-Stack Scaffolder**. You orchestrate two specialist agents to build a feature end-to-end across the Notify API and Admin codebases.
9+
10+
## Constraints
11+
12+
- Both the `notification-api` and `notification-admin` repos MUST be open in the current workspace (multi-root workspace).
13+
- ALWAYS run Phase 1 completely before starting Phase 2.
14+
- DO NOT write code yourself — delegate ALL implementation to the specialist agents.
15+
- DO NOT skip phases or combine them.
16+
17+
## Workflow
18+
19+
### Phase 0: Gather Requirements
20+
21+
Before delegating, clarify with the user (or infer from their request):
22+
1. **Entity name** (singular + plural)
23+
2. **Fields** and their types
24+
3. **Operations needed** (GET one, GET all, POST create, POST update, DELETE)
25+
4. **Scope**: service-scoped (`/service/<service_id>/...`) or top-level
26+
5. **Auth type**: `requires_admin_auth`, `requires_auth`, or `requires_no_auth`
27+
6. **Admin UI operations**: list page, create form, edit form, delete, or just API client with no views
28+
29+
### Phase 1: API Endpoints (Notify API repo)
30+
31+
Delegate to the **notify-rest-endpoint-builder** agent with a prompt that includes:
32+
- The entity name, fields, and operations
33+
- Whether it's service-scoped or top-level
34+
- The auth type
35+
- An explicit instruction to **report back the full endpoint contract**: every URL path, HTTP method, request body schema, and response shape
36+
37+
Parse the agent's response and extract the endpoint contract for Phase 2.
38+
39+
### Phase 2: Admin API Client + Views (Notify Admin repo)
40+
41+
Delegate to the **admin-api-client-builder** agent with a prompt that includes:
42+
- The exact endpoint URLs, HTTP methods, and request/response shapes from Phase 1
43+
- Which Admin UI operations to create (list, create, edit, delete)
44+
- Whether to add to an existing API client or create a new one
45+
46+
### Phase 3: Summary
47+
48+
After both agents complete, output a **unified checklist** of all files created or edited across both repos, grouped by repo:
49+
50+
## Notify API
51+
file1 — description
52+
file2 — description
53+
## Notify Admin
54+
file3 — description
55+
file4 — description
56+
57+
## Output Format
58+
59+
Always end with the unified checklist. Include the endpoint contract (URLs + methods) in the summary so developers can verify the API ↔ Admin wiring matches.
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
name: Cypress staging run (reusable)
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
spec:
7+
description: Cypress spec file(s) to run (newline-separated)
8+
required: true
9+
type: string
10+
slack_failure_message:
11+
description: Parent Slack message text posted on failure
12+
required: false
13+
type: string
14+
report_to_slack:
15+
description: Whether to post failure notifications to Slack
16+
required: false
17+
type: boolean
18+
default: true
19+
secrets:
20+
CYPRESS_ENV_JSON:
21+
required: true
22+
SLACK_BOT_TOKEN:
23+
required: false
24+
SLACK_CHANNEL:
25+
required: false
26+
27+
defaults:
28+
run:
29+
shell: bash
30+
31+
jobs:
32+
cypress-tests:
33+
runs-on: ubuntu-latest
34+
continue-on-error: true
35+
steps:
36+
- name: checkout
37+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
38+
- uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2
39+
with:
40+
node-version: 22.22.3
41+
42+
- name: Write the cypress.env.json file
43+
run: |
44+
echo '${{ secrets.CYPRESS_ENV_JSON }}' > tests_cypress/cypress.env.json
45+
46+
- name: Run the cypress tests
47+
uses: cypress-io/github-action@248bde77443c376edc45906ede03a1aba9da0462 # v5.8.4
48+
with:
49+
record: false
50+
config: "video=true,screenshotOnRunFailure=true"
51+
build: npx cypress info
52+
working-directory: tests_cypress
53+
spec: ${{ inputs.spec }}
54+
55+
- name: Upload test artifacts
56+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
57+
if: always()
58+
with:
59+
name: cypress-artifacts
60+
path: |
61+
tests_cypress/cypress/videos
62+
tests_cypress/cypress/screenshots
63+
tests_cypress/cypress/results
64+
retention-days: 30
65+
66+
- name: Install jq for Slack notifications
67+
if: failure() && inputs.report_to_slack
68+
run: sudo apt-get update && sudo apt-get install -y jq
69+
70+
- name: Build failure previews from mochawesome
71+
if: failure() && inputs.report_to_slack
72+
run: |
73+
set -euo pipefail
74+
RESULTS_DIR=tests_cypress/cypress/results
75+
TMP=/tmp/failures.json
76+
77+
mapfile -t files < <(find "$RESULTS_DIR" -maxdepth 1 -type f -name 'mochawesome*.json' -print 2>/dev/null || true)
78+
79+
if [ "${#files[@]}" -eq 0 ]; then
80+
echo "No mochawesome files found in $RESULTS_DIR" >&2
81+
echo "failures=[]" > "$TMP"
82+
exit 0
83+
fi
84+
85+
valid_files=()
86+
for f in "${files[@]}"; do
87+
if jq -e 'has("results")' "$f" >/dev/null 2>&1; then
88+
valid_files+=("$f")
89+
else
90+
echo "Skipping non-mochawesome JSON: $f" >&2
91+
fi
92+
done
93+
94+
if [ "${#valid_files[@]}" -eq 0 ]; then
95+
echo "No valid mochawesome JSONs found" >&2
96+
echo "failures=[]" > "$TMP"
97+
exit 0
98+
fi
99+
100+
jq -s -c '[ .[] | .results[]? | .. | objects
101+
| select(.state=="failed")
102+
| { fullTitle: (.fullTitle // .title), message: (.err.message // .err.estack // "No error") }
103+
]
104+
| unique_by(.fullTitle)
105+
| map({ title: .fullTitle, preview: ( .message
106+
| split("\n")
107+
| map(gsub("^\\s+|\\s+$"; ""))
108+
| map(select(length>0))
109+
| .[0:5]
110+
| map("> " + .)
111+
| join("\n") ) })
112+
| .[0:5]' "${valid_files[@]}" > "$TMP" || echo "failures=[]" > "$TMP"
113+
114+
jq . "$TMP" || true
115+
116+
- name: Fetch artifact download URL
117+
if: failure() && inputs.report_to_slack
118+
id: artifact_link
119+
env:
120+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
121+
run: |
122+
set -euo pipefail
123+
resp=$(curl -s -H "Authorization: Bearer ${GITHUB_TOKEN}" "https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts")
124+
artifact_id=$(echo "$resp" | jq -r '.artifacts[0].id // empty')
125+
if [ -n "$artifact_id" ]; then
126+
url="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/$artifact_id"
127+
else
128+
url=''
129+
fi
130+
echo "artifact_url=$url" >> $GITHUB_OUTPUT
131+
132+
- name: Post parent message (chat.postMessage)
133+
if: failure() && inputs.report_to_slack
134+
id: post_parent
135+
env:
136+
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
137+
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
138+
run: |
139+
set -euo pipefail
140+
if [ -z "${SLACK_BOT_TOKEN:-}" ] || [ -z "${SLACK_CHANNEL:-}" ]; then
141+
echo "SLACK_BOT_TOKEN or SLACK_CHANNEL secret missing" >&2
142+
exit 1
143+
fi
144+
145+
PARENT_TEXT='*:rotating_light: ${{ inputs.slack_failure_message }} :rotating_light:*'
146+
RUN_LINK='https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}'
147+
LINK_TEXT=':github: Workflow run'
148+
ARTIFACTS_LINK='${{ steps.artifact_link.outputs.artifact_url }}'
149+
ARTIFACTS_TEXT=':file_cabinet: Artifacts download'
150+
if [ -z "$ARTIFACTS_LINK" ]; then
151+
ARTIFACTS_LINK='https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts'
152+
ARTIFACTS_TEXT=':file_cabinet: Artifacts'
153+
fi
154+
printf -v FULL_TEXT '%s\n<%s|%s>\n<%s|%s>' "$PARENT_TEXT" "$RUN_LINK" "$LINK_TEXT" "$ARTIFACTS_LINK" "$ARTIFACTS_TEXT"
155+
156+
resp=$(curl -s -X POST \
157+
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" \
158+
-H "Content-type: application/json" \
159+
--data "$(jq -nc --arg ch "$SLACK_CHANNEL" --arg txt "$FULL_TEXT" '{channel:$ch, text:$txt, mrkdwn:true}')" \
160+
https://slack.com/api/chat.postMessage)
161+
162+
echo "Parent response: $resp"
163+
ok=$(echo "$resp" | jq -r '.ok')
164+
if [ "$ok" != "true" ]; then
165+
echo "Failed to post parent message: $(echo "$resp" | jq -r '.error')" >&2
166+
exit 1
167+
fi
168+
169+
ts=$(echo "$resp" | jq -r '.ts')
170+
echo "ts=$ts" >> $GITHUB_OUTPUT
171+
172+
- name: Post threaded replies (max 5)
173+
if: failure() && inputs.report_to_slack
174+
env:
175+
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
176+
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
177+
run: |
178+
set -euo pipefail
179+
TS="${{ steps.post_parent.outputs.ts }}"
180+
if [ -z "$TS" ]; then
181+
echo "No parent ts, skipping replies"
182+
exit 0
183+
fi
184+
185+
mapfile -t items < <(jq -c '.[]' /tmp/failures.json)
186+
emojis=(":one:" ":two:" ":three:" ":four:" ":five:")
187+
for idx in "${!items[@]}"; do
188+
item=${items[$idx]}
189+
title=$(echo "$item" | jq -r '.title')
190+
preview=$(echo "$item" | jq -r '.preview')
191+
prefix=${emojis[$idx]:-":hash:"}
192+
printf -v prefixed_title '%s Test *`%s`* failed' "$prefix" "$title"
193+
body=$(jq -nc --arg ch "$SLACK_CHANNEL" --arg thread "$TS" --arg text "$prefixed_title" --arg preview "$preview" '{channel:$ch, text: ($text + "\n\n" + $preview + "\n\n:wavy_dash:\n"), thread_ts:$thread, mrkdwn:true}')
194+
195+
echo "Posting reply for $title"
196+
curl -s -X POST -H "Authorization: Bearer ${SLACK_BOT_TOKEN}" -H "Content-type: application/json" --data "$body" https://slack.com/api/chat.postMessage | jq . || true
197+
sleep 0.5
198+
done

.github/workflows/ai-code-scanner.yml

Lines changed: 0 additions & 26 deletions
This file was deleted.

.github/workflows/backstage-catalog-helper.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,27 @@ name: Backstage Catalog Info Helper
22
on:
33
workflow_dispatch:
44

5+
permissions:
6+
contents: write
7+
pull-requests: write
8+
59
jobs:
610
update-catalog-info:
711
runs-on: ubuntu-latest
812
steps:
13+
- name: Audit DNS requests
14+
uses: cds-snc/dns-proxy-action@f0796e7f3d6bec5d40aecb0321ed8012f5602f84 # v1.0.2
15+
env:
16+
DNS_PROXY_FORWARDTOSENTINEL: "true"
17+
DNS_PROXY_LOGANALYTICSWORKSPACEID: ${{ secrets.LOG_ANALYTICS_WORKSPACE_ID }}
18+
DNS_PROXY_LOGANALYTICSSHAREDKEY: ${{ secrets.LOG_ANALYTICS_WORKSPACE_KEY }}
919
- name: Checkout Actions
1020
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
1121
with:
1222
fetch-depth: 0
23+
persist-credentials: false
1324
- name: Run Backstage Catalog Info Helper
14-
uses: cds-snc/backstage-catalog-info-helper-action@v0.3.1
25+
uses: cds-snc/backstage-catalog-info-helper-action@cc75afc29a0ade6c41400132ff9e1222f8916ba6 # v0.3.1
1526
with:
1627
github_app_id: ${{ secrets.SRE_BOT_RW_APP_ID }}
1728
github_app_private_key: ${{ secrets.SRE_BOT_RW_PRIVATE_KEY }}

.github/workflows/call-manifests-update-docker-tag.yaml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@ name: Call Manifests Update Docker Tag
22

33
on:
44
workflow_dispatch:
5-
push:
6-
branches:
7-
- main
5+
workflow_call:
6+
secrets:
7+
MANIFESTS_WORKFLOW_TOKEN:
8+
required: true
89

910
jobs:
1011
update-docker-tag-in-manifests:
1112
runs-on: ubuntu-latest
1213

1314
steps:
1415
- name: Checkout repository
15-
uses: actions/checkout@v2
16+
uses: actions/checkout@0717577d45739eb3c851188b29f50ed6c0b2194e # v2.8.0
1617

1718
- name: Set Docker Tag
1819
run: echo "DOCKER_TAG=${GITHUB_SHA::7}" >> $GITHUB_ENV

0 commit comments

Comments
 (0)