Skip to content

Commit 9be165a

Browse files
authored
Merge pull request #536 from Nicxe/codex/phase-9-stable-promotion
Promote beta to stable main
2 parents a5c0c75 + e84b17e commit 9be165a

94 files changed

Lines changed: 37855 additions & 1143 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.

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!--
22
Target the correct branch:
33
- Docs or blueprint changes (standalone, no code) → `content` branch
4-
- Code changes (integration, sensors, fixes, features, tests) → `dev` branch
4+
- Code changes (integration, sensors, bundled card code, fixes, features, tests) → `dev` branch
55
- PRs targeting `main` or `beta` are closed automatically.
66
If you are unsure whether your change fits the project direction, open an issue first.
77
-->
@@ -21,6 +21,7 @@ Fixes #
2121
- [ ] 🐛 Bug fix (corrects existing behavior without breaking anything)
2222
- [ ] 🚀 New feature (adds functionality)
2323
- [ ] ⚠️ Breaking change (existing automations, entities, or config will stop working or behave differently)
24+
- [ ] 🏎️ Bundled live data card code
2425
- [ ] 📋 Blueprint (new or updated blueprint)
2526
- [ ] 📚 Documentation only
2627
- [ ] 🔧 Refactoring or internal cleanup
@@ -35,6 +36,7 @@ Fixes #
3536
## Checklist
3637

3738
- [ ] I have read [CONTRIBUTING.md](../CONTRIBUTING.md) and am targeting the correct branch
39+
- [ ] Changes under `custom_components/f1_sensor/www/**` target `dev` as bundled card code, not the standalone documentation path — if applicable
3840
- [ ] Code is formatted and passes lint check (`ruff format` and `ruff check`) — if applicable
3941
- [ ] Tests have been added or updated and pass locally — if applicable
4042
- [ ] Translations updated if new UI strings were added — if applicable

.github/labeler.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ tests:
1010
- any-glob-to-any-file:
1111
- "custom_components/f1_sensor/tests/**"
1212

13+
card:
14+
- changed-files:
15+
- any-glob-to-any-file:
16+
- "custom_components/f1_sensor/www/**"
17+
1318
blueprint:
1419
- changed-files:
1520
- any-glob-to-any-file:

.github/workflows/conflict-check.yml

Lines changed: 88 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,99 @@ on:
88

99
permissions:
1010
contents: read
11+
issues: write
1112
pull-requests: write
1213

1314
jobs:
1415
conflict:
1516
name: Check for merge conflicts
1617
runs-on: ubuntu-latest
1718
steps:
18-
- uses: eps1lon/actions-label-merge-conflict@v3
19+
- uses: actions/github-script@v8
1920
with:
20-
dirtyLabel: "has-conflicts"
21-
repoToken: ${{ secrets.GITHUB_TOKEN }}
22-
commentOnDirty: |
23-
This PR has merge conflicts with the target branch and cannot be merged until they are resolved.
24-
25-
Please rebase or merge the target branch into your branch and resolve the conflicts locally, then push the updated branch.
26-
commentOnClean: |
27-
Merge conflicts have been resolved. Thank you!
21+
script: |
22+
const dirtyLabel = "has-conflicts";
23+
const commentOnDirty = `This PR has merge conflicts with the target branch and cannot be merged until they are resolved.
24+
25+
Please rebase or merge the target branch into your branch and resolve the conflicts locally, then push the updated branch.`;
26+
const commentOnClean = "Merge conflicts have been resolved. Thank you!";
27+
const { owner, repo } = context.repo;
28+
29+
async function candidatePulls() {
30+
if (context.payload.pull_request) {
31+
return [context.payload.pull_request];
32+
}
33+
34+
const base = context.ref.replace("refs/heads/", "");
35+
return github.paginate(github.rest.pulls.list, {
36+
owner,
37+
repo,
38+
state: "open",
39+
base,
40+
per_page: 100,
41+
});
42+
}
43+
44+
async function pullWithMergeableState(number) {
45+
for (let attempt = 0; attempt < 10; attempt += 1) {
46+
const { data: pull } = await github.rest.pulls.get({
47+
owner,
48+
repo,
49+
pull_number: number,
50+
});
51+
52+
if (pull.mergeable !== null && pull.mergeable_state !== "unknown") {
53+
return pull;
54+
}
55+
56+
await new Promise((resolve) => setTimeout(resolve, 3000));
57+
}
58+
59+
core.warning(`Mergeability is still unknown for PR #${number}`);
60+
return null;
61+
}
62+
63+
async function updateConflictLabel(pull) {
64+
const current = await pullWithMergeableState(pull.number);
65+
if (current === null) {
66+
return;
67+
}
68+
69+
const labels = current.labels.map((label) => label.name);
70+
const hasDirtyLabel = labels.includes(dirtyLabel);
71+
const isDirty = current.mergeable_state === "dirty";
72+
73+
if (isDirty && !hasDirtyLabel) {
74+
await github.rest.issues.addLabels({
75+
owner,
76+
repo,
77+
issue_number: current.number,
78+
labels: [dirtyLabel],
79+
});
80+
await github.rest.issues.createComment({
81+
owner,
82+
repo,
83+
issue_number: current.number,
84+
body: commentOnDirty,
85+
});
86+
}
87+
88+
if (!isDirty && hasDirtyLabel) {
89+
await github.rest.issues.removeLabel({
90+
owner,
91+
repo,
92+
issue_number: current.number,
93+
name: dirtyLabel,
94+
});
95+
await github.rest.issues.createComment({
96+
owner,
97+
repo,
98+
issue_number: current.number,
99+
body: commentOnClean,
100+
});
101+
}
102+
}
103+
104+
for (const pull of await candidatePulls()) {
105+
await updateConflictLabel(pull);
106+
}

.github/workflows/context7-refresh.yml

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,24 @@ jobs:
1212
steps:
1313
- name: Trigger Context7 re-index
1414
run: |
15-
curl -s -X POST https://context7.com/api/v1/refresh \
16-
-H "Authorization: Bearer ${{ secrets.CONTEXT7_API_KEY }}" \
17-
-H "Content-Type: application/json" \
18-
-d '{"libraryName": "/websites/nicxe_github_io_f1_sensor"}' \
19-
--fail-with-body
15+
for i in 1 2 3; do
16+
response=$(curl -s -w "\n%{http_code}" -X POST https://context7.com/api/v1/refresh \
17+
-H "Authorization: Bearer ${{ secrets.CONTEXT7_API_KEY }}" \
18+
-H "Content-Type: application/json" \
19+
-d '{"libraryName": "/websites/nicxe_github_io_f1_sensor"}')
20+
http_code=$(echo "$response" | tail -1)
21+
body=$(echo "$response" | sed '$d')
22+
if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
23+
echo "Success: $body"
24+
exit 0
25+
fi
26+
if echo "$body" | grep -q "user-has-active-task"; then
27+
echo "Another indexing job is running, retrying in 30s... (attempt $i/3)"
28+
sleep 30
29+
continue
30+
fi
31+
echo "Request failed ($http_code): $body"
32+
exit 1
33+
done
34+
echo "Context7 still busy after 3 attempts, giving up."
35+
exit 1

.github/workflows/validate.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,25 @@ concurrency:
1414
cancel-in-progress: true
1515

1616
jobs:
17+
card-assets:
18+
name: Validate bundled card assets
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v6
22+
23+
- name: Set up Node
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version: "22"
27+
28+
- name: Check bundled live data card syntax
29+
run: >
30+
node --check
31+
custom_components/f1_sensor/www/f1-sensor-live-data-card/f1-sensor-live-data-card.js
32+
1733
validate:
1834
uses: nicxe/.github/.github/workflows/reusable-validate-integration.yml@main
1935
with:
2036
hacs_category: "integration"
2137
run_hassfest: true
22-
secrets: inherit
38+
secrets: inherit

CONTRIBUTING.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ This project uses two contribution paths depending on what you are changing.
88

99
### Code changes
1010

11-
For changes to the integration itself — sensors, binary sensors, configuration flow, coordinator logic, fixes, features, tests — use the code path:
11+
For changes to the integration itself — sensors, binary sensors, configuration flow, coordinator logic, bundled Live Data Card code, fixes, features, tests — use the code path:
1212

1313
- `dev` — the active development branch. All code contributions must target this branch.
1414
- `beta` — pre-release testing. Promoted from `dev` by the maintainer.
@@ -29,6 +29,7 @@ No version bump or release is triggered when only documentation or blueprint fil
2929
| What I am changing | Target branch |
3030
|---|---|
3131
| Integration code, sensors, fixes, features | `dev` |
32+
| Bundled Live Data Card code in `custom_components/f1_sensor/www/**` | `dev` |
3233
| Tests only | `dev` |
3334
| Documentation for an upcoming code change | `dev` (keep docs with the code) |
3435
| Standalone documentation fix or update | `content` |

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ Whether you want your lights to react to a safety car or build dashboards that v
2525

2626
### 🃏 Live Data Cards
2727

28-
A companion collection of custom Lovelace cards is available for displaying live session data directly on your dashboards — tyre strategy, pit stops, lap times, investigations, race control, and more.
28+
F1 Sensor includes custom Lovelace cards for displaying live session data directly on your dashboards — tyre strategy, pit stops, lap times, investigations, race control, and more.
2929

3030
<img alt="F1 Sensor Live Data Cards" src="https://github.com/user-attachments/assets/9ec7b6c0-9584-4647-baac-03407d73ef99" />
3131

32-
👉 **[github.com/Nicxe/f1-sensor-live-data-card](https://github.com/Nicxe/f1-sensor-live-data-card)**
32+
The card assets are bundled with the integration and registered automatically in Home Assistant. Existing dashboard card types stay the same, so you can keep your current dashboard configuration after updating.
3333

3434
---
3535

@@ -54,6 +54,7 @@ If you enjoy using `F1 Sensor`, you can find ways to [**Support the project here
5454
<br>
5555
<br>
5656
<br>
57+
<br>
5758

5859
> [!NOTE]
5960
> F1 Sensor is an unofficial project and is not associated in any way with the Formula 1 companies. F1, FORMULA ONE, FORMULA 1, FIA FORMULA ONE WORLD CHAMPIONSHIP, GRAND PRIX and related marks are trade marks of Formula One Licensing B.V.

0 commit comments

Comments
 (0)