Skip to content

Commit 37575d1

Browse files
authored
Merge branch 'master' into copilot/fix-253
2 parents 65a94f6 + 09e0a18 commit 37575d1

File tree

7 files changed

+197
-3
lines changed

7 files changed

+197
-3
lines changed

.eleventyignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
./_image_sources
33
./_drafts
44
./tests
5-
5+
./.github
66
Dockerfile
77
DOCKER_OPTIMIZATION.md

.github/scripts/README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# GitHub Scripts and Automation
2+
3+
This directory contains scripts and documentation for GitHub Actions automation in the Orionrobots repository.
4+
5+
## Features
6+
7+
### Automated Docker Image PR Comments
8+
9+
When a Pull Request modifies files that affect the base Docker image (such as `Dockerfile`, `package.json`, `package-lock.json`, or workflow files), the CI system automatically:
10+
11+
1. **Detects changes** to base image related files
12+
2. **Builds and pushes** a Docker image tagged with the PR number to `ghcr.io/orionrobots/orionrobots-site.base:${PR_NUMBER}`
13+
3. **Comments on the PR** with a direct link to the newly built Docker image
14+
15+
#### How it works
16+
17+
The automation is implemented in `.github/workflows/on_pr_test.yaml`:
18+
19+
- **Detection**: The `detect_base_image_changes` job uses `tj-actions/changed-files` to detect changes to base image files
20+
- **Build**: If changes are detected, the `build_site` job builds and pushes the image with the PR number as tag
21+
- **Comment**: The `comment_docker_image` job creates or updates a comment on the PR with the image details
22+
23+
#### Benefits
24+
25+
- **Easy access**: Reviewers and team members can quickly find and use the Docker image built for a specific PR
26+
- **No searching**: No need to dig through workflow logs or GitHub Package registry
27+
- **Idempotent**: Comments are updated rather than duplicated when the image is rebuilt
28+
- **Clear instructions**: The comment includes copy-paste commands for using the image
29+
30+
#### Comment format
31+
32+
The automated comment includes:
33+
- Direct link to the Docker image
34+
- Instructions for pulling and running the image
35+
- Usage examples for local development
36+
- Clear indication that the comment is automatically managed
37+
38+
#### Permissions and fork compatibility
39+
40+
- **Internal PRs**: Full functionality with automatic image building and commenting
41+
- **Forks**: May have limited access to push images depending on repository settings
42+
- **Security**: Uses minimal required permissions (`pull-requests: write` for commenting)
43+
44+
### Scripts
45+
46+
- `new_post.sh`: Script for creating new blog posts with proper folder structure
47+
- `staging/`: Configuration files for staging environment setup
48+
49+
## Maintenance
50+
51+
The Docker image commenting system is self-maintaining and requires no manual intervention. If issues arise:
52+
53+
1. Check the workflow logs in GitHub Actions
54+
2. Verify that the GitHub token has appropriate permissions
55+
3. Ensure the base image build completed successfully before the comment job runs

.github/workflows/on_call_staging_test.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ jobs:
4646
4747
# Perform the tests
4848
set +e # Don't exit on error so we can clean up
49+
set -o pipefail # Ensure pipeline returns exit code of first failing command
4950
exit_code=0
5051
5152
curl -I -f http://localhost:8080/ || exit_code=1

.github/workflows/on_pr_test.yaml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ on:
1919
- '.github/workflows/on_push_to_master_test_and_deploy.yaml'
2020
- '.github/workflows/on_call_build_site.yaml'
2121
- '.github/workflows/on_call_staging_test.yaml'
22+
- '.github/workflows/on_pr_test.yaml'
2223
- 'package.json'
2324
- 'package-lock.json'
2425
- 'webpack.config.js'
@@ -58,6 +59,76 @@ jobs:
5859
${{ needs.detect_base_image_changes.outputs.changed == 'true'
5960
&& github.event.number || '' }}
6061
62+
comment_docker_image:
63+
needs: [detect_base_image_changes, build_site]
64+
runs-on: ubuntu-latest
65+
# Only run if base image changes were detected and this is a PR event
66+
if: needs.detect_base_image_changes.outputs.changed == 'true' && github.event_name == 'pull_request'
67+
permissions:
68+
pull-requests: write # Allow commenting on PR
69+
steps:
70+
- name: Comment on PR with Docker image link
71+
uses: actions/github-script@v7
72+
with:
73+
script: |
74+
const prNumber = context.issue.number;
75+
const imageTag = prNumber;
76+
const imageUrl = `ghcr.io/orionrobots/orionrobots-site.base:${imageTag}`;
77+
78+
// Comment body with Docker image information
79+
const commentBody = `## 🐳 Docker Base Image Available
80+
81+
A new base Docker image has been built and pushed for this PR:
82+
83+
**Image:** \`${imageUrl}\`
84+
85+
### How to use this image:
86+
87+
\`\`\`bash
88+
# Pull the image
89+
docker pull ${imageUrl}
90+
91+
# Run with the image
92+
docker run -it ${imageUrl} bash
93+
\`\`\`
94+
95+
### For local development:
96+
You can use this image as a base for testing changes without rebuilding dependencies.
97+
98+
_This comment is automatically updated when the base image is rebuilt._`;
99+
100+
// Look for existing comment from this bot
101+
const comments = await github.rest.issues.listComments({
102+
owner: context.repo.owner,
103+
repo: context.repo.repo,
104+
issue_number: prNumber,
105+
});
106+
107+
const botComment = comments.data.find(comment =>
108+
comment.user.type === 'Bot' &&
109+
comment.body.includes('🐳 Docker Base Image Available')
110+
);
111+
112+
if (botComment) {
113+
// Update existing comment
114+
await github.rest.issues.updateComment({
115+
owner: context.repo.owner,
116+
repo: context.repo.repo,
117+
comment_id: botComment.id,
118+
body: commentBody
119+
});
120+
console.log('Updated existing Docker image comment');
121+
} else {
122+
// Create new comment
123+
await github.rest.issues.createComment({
124+
owner: context.repo.owner,
125+
repo: context.repo.repo,
126+
issue_number: prNumber,
127+
body: commentBody
128+
});
129+
console.log('Created new Docker image comment');
130+
}
131+
61132
staging_test:
62133
uses: ./.github/workflows/on_call_staging_test.yaml
63134
needs: build_site

src/thumbnails.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,21 @@ function has_thumbnail(post) {
3636

3737
async function thumbnail_for_post(post) {
3838
const thumbnailUrl = getPostThumbnailPath(post);
39+
let imageSrc;
40+
3941
if(thumbnailUrl == undefined) {
40-
return "";
42+
// Use favicon.png as fallback when no thumbnail is defined
43+
imageSrc = "favicon.png";
44+
} else {
45+
imageSrc = stripLeadingSlash(thumbnailUrl);
46+
if ( !fs.existsSync(imageSrc)) {
47+
// Use favicon.png as fallback when thumbnail file doesn't exist
48+
imageSrc = "favicon.png";
49+
}
4150
}
42-
const imageSrc = stripLeadingSlash(thumbnailUrl);
51+
4352
if ( !fs.existsSync(imageSrc)) {
53+
// If even favicon.png doesn't exist, return empty (shouldn't happen)
4454
return "";
4555
} else {
4656
// console.log("Generating thumbnail for " + imageSrc);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Feature: Recent Posts Thumbnails
2+
3+
Scenario: All recent posts should have thumbnail images
4+
Given the Staging site is started
5+
When the index page is visited
6+
Then the recent posts list should be present
7+
And each recent post should have a picture tag with an img element

tests/staging/step_definitions/website_steps.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,53 @@ Then('the Logo should be displayed in the top left corner', async function () {
7676
throw new Error('Logo (favicon.png) is not displayed in a nav element');
7777
}
7878
});
79+
80+
Then('the recent posts list should be present', async function () {
81+
if (!page) {
82+
throw new Error('Page not initialized. Make sure previous steps are executed first.');
83+
}
84+
85+
// Check that the recent posts section exists with its heading
86+
try {
87+
const recentPostsHeading = await page.locator('h2:has-text("Recent Posts")');
88+
await recentPostsHeading.waitFor({ state: 'visible', timeout: 10000 });
89+
90+
// Check that the posts list exists
91+
const postsList = await page.locator('ul.posts');
92+
await postsList.waitFor({ state: 'visible', timeout: 10000 });
93+
} catch (error) {
94+
throw new Error('Recent posts list is not present on the page');
95+
}
96+
});
97+
98+
Then('each recent post should have a picture tag with an img element', async function () {
99+
if (!page) {
100+
throw new Error('Page not initialized. Make sure previous steps are executed first.');
101+
}
102+
103+
// Find all post items in the recent posts list
104+
const postItems = await page.locator('ul.posts li.post').all();
105+
106+
if (postItems.length === 0) {
107+
throw new Error('No recent posts found in the posts list');
108+
}
109+
110+
// Check each post item has a picture tag with img element
111+
for (let i = 0; i < postItems.length; i++) {
112+
const postItem = postItems[i];
113+
114+
try {
115+
// Look for either a picture tag with img, or just an img tag within the media-left link
116+
const imageElement = await postItem.locator('a.media-left picture img, a.media-left img').first();
117+
await imageElement.waitFor({ state: 'visible', timeout: 5000 });
118+
119+
// Verify the img element has a valid src attribute
120+
const src = await imageElement.getAttribute('src');
121+
if (!src || src.trim() === '') {
122+
throw new Error(`Post ${i + 1} has an img element but no src attribute`);
123+
}
124+
} catch (error) {
125+
throw new Error(`Post ${i + 1} does not have a picture tag with img element: ${error.message}`);
126+
}
127+
}
128+
});

0 commit comments

Comments
 (0)