Skip to content

Commit 7a93a83

Browse files
authored
Merge branch 'master' into perf/kanvas-lazy-load
2 parents ffff23f + 2b62849 commit 7a93a83

File tree

59 files changed

+535
-52
lines changed

Some content is hidden

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

59 files changed

+535
-52
lines changed

.github/workflows/build-and-preview-site.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Install and Build 🔧
1818
run: |
1919
make setup
20-
make site-full
20+
make build
2121
2222
- name: Broken Link Check 🔗
2323
uses: technote-space/broken-link-checker-action@v2
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
# =====================================================================================
2+
# Copilot SWE Agent PR Handler
3+
# =====================================================================================
4+
# This workflow automatically handles pull requests created by the GitHub Copilot
5+
# SWE Agent (https://github.com/apps/copilot-swe-agent).
6+
#
7+
# It performs two key actions:
8+
# 1. Marks draft PRs as "ready for review"
9+
# 2. Approves pending workflow runs for the PR branch
10+
#
11+
# This is necessary because:
12+
# - PRs from first-time contributors (including bots) require manual approval
13+
# to run workflows for security reasons
14+
# - The Copilot agent creates draft PRs that need to be marked as ready
15+
# =====================================================================================
16+
17+
name: Copilot PR Handler
18+
19+
on:
20+
# Use pull_request_target to get write permissions for PRs from forks/bots
21+
# This is safe here because we're only performing administrative actions,
22+
# not checking out or running code from the PR
23+
pull_request_target:
24+
types: [opened, synchronize, reopened]
25+
branches:
26+
- master
27+
28+
# Allow manual triggering for testing and debugging
29+
workflow_dispatch:
30+
inputs:
31+
pr_number:
32+
description: 'PR number to process (for manual testing)'
33+
required: false
34+
type: string
35+
debug_mode:
36+
description: 'Enable verbose debug logging'
37+
required: false
38+
default: 'false'
39+
type: boolean
40+
41+
# Minimal permissions required for this workflow
42+
# - actions: write - Required to approve workflow runs
43+
# - pull-requests: write - Required to mark PRs as ready for review
44+
# - contents: read - Required to access repository content
45+
permissions:
46+
actions: write
47+
pull-requests: write
48+
contents: read
49+
50+
jobs:
51+
handle-copilot-pr:
52+
name: Handle Copilot PR
53+
runs-on: ubuntu-24.04
54+
# Only run for the meshery/meshery repository
55+
# Only run for PRs from the Copilot SWE agent (copilot[bot])
56+
if: |
57+
github.repository == 'meshery/meshery' &&
58+
(
59+
github.event_name == 'workflow_dispatch' ||
60+
github.event.pull_request.user.login == 'copilot[bot]'
61+
)
62+
63+
steps:
64+
# -------------------------------------------------------------------------
65+
# Step 1: Introspect and log all relevant context for debugging
66+
# -------------------------------------------------------------------------
67+
- name: 🔍 Introspect Inputs and Context
68+
run: |
69+
echo "::group::Workflow Context"
70+
echo "Event Name: ${{ github.event_name }}"
71+
echo "Actor: ${{ github.actor }}"
72+
echo "Repository: ${{ github.repository }}"
73+
echo "::endgroup::"
74+
75+
echo "::group::Pull Request Information"
76+
echo "PR Number: ${{ github.event.pull_request.number || inputs.pr_number || 'N/A' }}"
77+
echo "PR Author: ${{ github.event.pull_request.user.login || 'N/A' }}"
78+
echo "PR Draft Status: ${{ github.event.pull_request.draft || 'N/A' }}"
79+
echo "PR Head SHA: ${{ github.event.pull_request.head.sha || 'N/A' }}"
80+
echo "PR Head Ref: ${{ github.event.pull_request.head.ref || 'N/A' }}"
81+
echo "::endgroup::"
82+
83+
echo "::group::Debug Settings"
84+
echo "Debug Mode: ${{ inputs.debug_mode || 'false' }}"
85+
echo "::endgroup::"
86+
87+
# -------------------------------------------------------------------------
88+
# Step 2: Mark PR as ready for review if it's in draft state
89+
# -------------------------------------------------------------------------
90+
- name: 📝 Mark PR as Ready for Review
91+
uses: actions/github-script@v7
92+
with:
93+
github-token: ${{ secrets.GH_ACCESS_TOKEN }}
94+
script: |
95+
const prNumber = context.payload.pull_request?.number || parseInt('${{ inputs.pr_number }}') || null;
96+
97+
if (!prNumber) {
98+
core.info('No PR number available, skipping ready for review step');
99+
return;
100+
}
101+
102+
core.info(`Processing PR #${prNumber}`);
103+
104+
try {
105+
// Get PR details to check if it's a draft
106+
const { data: pr } = await github.rest.pulls.get({
107+
owner: context.repo.owner,
108+
repo: context.repo.repo,
109+
pull_number: prNumber
110+
});
111+
112+
core.info(`PR #${prNumber} draft status: ${pr.draft}`);
113+
114+
if (pr.draft) {
115+
core.info(`Marking PR #${prNumber} as ready for review...`);
116+
117+
// Use GraphQL API to mark as ready for review
118+
// The REST API doesn't support this operation
119+
await github.graphql(`
120+
mutation($pullRequestId: ID!) {
121+
markPullRequestReadyForReview(input: {pullRequestId: $pullRequestId}) {
122+
pullRequest {
123+
isDraft
124+
}
125+
}
126+
}
127+
`, {
128+
pullRequestId: pr.node_id
129+
});
130+
131+
core.info(`✅ PR #${prNumber} has been marked as ready for review`);
132+
} else {
133+
core.info(`PR #${prNumber} is already marked as ready for review`);
134+
}
135+
} catch (error) {
136+
core.warning(`Failed to mark PR as ready for review: ${error.message}`);
137+
// Don't fail the workflow, continue to next step
138+
}
139+
140+
# -------------------------------------------------------------------------
141+
# Step 3: Approve pending workflow runs for this PR
142+
# -------------------------------------------------------------------------
143+
- name: ✅ Approve Pending Workflow Runs
144+
uses: actions/github-script@v7
145+
with:
146+
github-token: ${{ secrets.GH_ACCESS_TOKEN }}
147+
script: |
148+
const prNumber = context.payload.pull_request?.number || parseInt('${{ inputs.pr_number }}') || null;
149+
const headSha = context.payload.pull_request?.head?.sha || null;
150+
const headRef = context.payload.pull_request?.head?.ref || null;
151+
152+
if (!headRef && !headSha) {
153+
core.info('No head ref or SHA available, skipping workflow approval step');
154+
return;
155+
}
156+
157+
core.info(`Looking for pending workflow runs for PR #${prNumber || 'N/A'}`);
158+
core.info(`Head SHA: ${headSha || 'N/A'}, Head Ref: ${headRef || 'N/A'}`);
159+
160+
try {
161+
// List workflow runs that are pending approval
162+
// These are runs with status 'action_required' (waiting for approval)
163+
const { data: runs } = await github.rest.actions.listWorkflowRunsForRepo({
164+
owner: context.repo.owner,
165+
repo: context.repo.repo,
166+
status: 'action_required',
167+
per_page: 100
168+
});
169+
170+
core.info(`Found ${runs.total_count} workflow run(s) awaiting approval`);
171+
172+
// Filter runs for this PR's branch/SHA
173+
const pendingRuns = runs.workflow_runs.filter(run => {
174+
const matchesSha = headSha && run.head_sha === headSha;
175+
const matchesRef = headRef && run.head_branch === headRef;
176+
return matchesSha || matchesRef;
177+
});
178+
179+
core.info(`Found ${pendingRuns.length} pending run(s) for this PR`);
180+
181+
// Approve each pending run
182+
for (const run of pendingRuns) {
183+
core.info(`Approving workflow run: ${run.name} (ID: ${run.id})`);
184+
185+
try {
186+
await github.rest.actions.approveWorkflowRun({
187+
owner: context.repo.owner,
188+
repo: context.repo.repo,
189+
run_id: run.id
190+
});
191+
core.info(`✅ Approved workflow run: ${run.name} (ID: ${run.id})`);
192+
} catch (approvalError) {
193+
core.warning(`Failed to approve run ${run.id}: ${approvalError.message}`);
194+
}
195+
}
196+
197+
if (pendingRuns.length === 0) {
198+
core.info('No pending workflow runs found for this PR');
199+
}
200+
} catch (error) {
201+
core.warning(`Failed to approve workflow runs: ${error.message}`);
202+
// Don't fail the workflow
203+
}
204+
205+
# -------------------------------------------------------------------------
206+
# Step 4: Post status comment on the PR
207+
# -------------------------------------------------------------------------
208+
- name: 📢 Post Status Comment
209+
if: always()
210+
uses: actions/github-script@v7
211+
with:
212+
github-token: ${{ secrets.GH_ACCESS_TOKEN }}
213+
script: |
214+
const prNumber = context.payload.pull_request?.number || parseInt('${{ inputs.pr_number }}') || null;
215+
216+
if (!prNumber) {
217+
core.info('No PR number available, skipping status comment');
218+
return;
219+
}
220+
221+
const jobStatus = '${{ job.status }}';
222+
const statusEmoji = jobStatus === 'success' ? '✅' : jobStatus === 'failure' ? '❌' : '⚠️';
223+
224+
// Only comment on success to avoid noise
225+
if (jobStatus === 'success') {
226+
const body = `### ${statusEmoji} Copilot PR Handler
227+
228+
This pull request from GitHub Copilot has been automatically processed:
229+
- ✅ Marked as ready for review (if it was a draft)
230+
- ✅ Approved pending workflow runs
231+
232+
The CI checks should now run automatically.`;
233+
234+
try {
235+
await github.rest.issues.createComment({
236+
owner: context.repo.owner,
237+
repo: context.repo.repo,
238+
issue_number: prNumber,
239+
body: body
240+
});
241+
core.info(`Posted status comment on PR #${prNumber}`);
242+
} catch (error) {
243+
core.warning(`Failed to post status comment: ${error.message}`);
244+
}
245+
}

gatsby-config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-env node */
22

33
const isProduction = process.env.NODE_ENV === "production";
4-
const isFullSiteBuild = process.env.BUILD_FULL_SITE !== "false";
4+
const isFullSiteBuild = process.env.BUILD_FULL_SITE === "true";
55
const HEAVY_COLLECTIONS = ["members", "integrations"];
66
const collectionIgnoreGlobs = isFullSiteBuild
77
? []

gatsby-node.js

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ if (process.env.CI === "true") {
3434
if (page.path !== oldPage.path) {
3535
// Replace new page with old page
3636
deletePage(oldPage);
37+
page.slices = { ...DEFAULT_SLICES, ...(page.slices || {}) };
3738
createPage(page);
3839

3940
createRedirect({
@@ -49,16 +50,52 @@ if (process.env.CI === "true") {
4950

5051
const { loadRedirects } = require("./src/utils/redirects.js");
5152

53+
const DEFAULT_SLICES = {
54+
"site-header": "site-header",
55+
"site-footer": "site-footer",
56+
"cta-bottom": "cta-bottom",
57+
"cta-fullwidth": "cta-fullwidth",
58+
"cta-imageonly": "cta-imageonly",
59+
};
60+
5261
exports.createPages = async ({ actions, graphql, reporter }) => {
53-
const { createRedirect } = actions;
62+
const { createRedirect, createSlice } = actions;
5463
const redirects = loadRedirects();
5564
redirects.forEach(redirect => createRedirect(redirect)); // Handles all hardcoded ones dynamically
5665
// Create Pages
5766
const { createPage } = actions;
5867

68+
createSlice({
69+
id: "site-header",
70+
component: path.resolve("./src/slices/site-header.js"),
71+
});
72+
73+
createSlice({
74+
id: "site-footer",
75+
component: path.resolve("./src/slices/site-footer.js"),
76+
});
77+
78+
createSlice({
79+
id: "cta-bottom",
80+
component: path.resolve("./src/slices/cta-bottom.js"),
81+
});
82+
83+
createSlice({
84+
id: "cta-fullwidth",
85+
component: path.resolve("./src/slices/cta-fullwidth.js"),
86+
});
87+
88+
createSlice({
89+
id: "cta-imageonly",
90+
component: path.resolve("./src/slices/cta-imageonly.js"),
91+
});
92+
5993
const envCreatePage = (props) => {
94+
const pageConfig = { ...props };
95+
pageConfig.slices = { ...DEFAULT_SLICES, ...(pageConfig.slices || {}) };
96+
6097
if (process.env.CI === "true") {
61-
const { path, matchPath, ...rest } = props;
98+
const { path, matchPath, ...rest } = pageConfig;
6299

63100
createRedirect({
64101
fromPath: `/${path}/`,
@@ -73,7 +110,7 @@ exports.createPages = async ({ actions, graphql, reporter }) => {
73110
...rest,
74111
});
75112
}
76-
return createPage(props);
113+
return createPage(pageConfig);
77114
};
78115

79116
const blogPostTemplate = path.resolve("src/templates/blog-single.js");
@@ -510,7 +547,7 @@ exports.createPages = async ({ actions, graphql, reporter }) => {
510547
});
511548

512549
const components = componentsData.map((component) => component.src.replace("/", ""));
513-
const createComponentPages = (createPage, components) => {
550+
const createComponentPages = (envCreatePage, components) => {
514551
const pageTypes = [
515552
{ suffix: "", file: "index.js" },
516553
{ suffix: "/guidance", file: "guidance.js" },
@@ -523,7 +560,7 @@ exports.createPages = async ({ actions, graphql, reporter }) => {
523560
const componentPath = `./src/sections/Projects/Sistent/components/${name}/${file}`;
524561
if (fs.existsSync(path.resolve(componentPath))) {
525562
try {
526-
createPage({
563+
envCreatePage({
527564
path: pagePath,
528565
component: require.resolve(componentPath),
529566
});
@@ -537,7 +574,7 @@ exports.createPages = async ({ actions, graphql, reporter }) => {
537574
});
538575
};
539576

540-
createComponentPages(createPage, components);
577+
createComponentPages(envCreatePage, components);
541578
};
542579

543580
// slug starts and ends with '/' so parts[0] and parts[-1] will be empty

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "layer5",
2+
"name": "Layer5",
33
"description": "Our cloud native application and infrastructure management software enables organizations to expect more from their infrastructure. At Layer5, we believe collaboration enables innovation, and infrastructure enables collaboration",
44
"version": "1.0.0",
55
"private": true,
@@ -10,9 +10,9 @@
1010
"url": "https://layer5.io"
1111
},
1212
"scripts": {
13-
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 gatsby build",
13+
"build": "cross-env BUILD_FULL_SITE=true NODE_OPTIONS=--max-old-space-size=8192 gatsby build",
1414
"clean": "gatsby clean && rimraf node_modules",
15-
"develop": "cross-env NODE_OPTIONS=--max-old-space-size=8192 env-cmd -f .env.development gatsby develop",
15+
"develop": "cross-env BUILD_FULL_SITE=true NODE_OPTIONS=--max-old-space-size=8192 env-cmd -f .env.development gatsby develop",
1616
"develop:lite": "cross-env BUILD_FULL_SITE=false NODE_OPTIONS=--max-old-space-size=8192 env-cmd -f .env.development gatsby develop",
1717
"dev": "npm run develop",
1818
"start": "npm run develop",

0 commit comments

Comments
 (0)