Skip to content

Commit 58f1e4d

Browse files
authored
Add RISE program page (#138)
1 parent d5cb230 commit 58f1e4d

6 files changed

Lines changed: 257 additions & 4 deletions

File tree

.github/workflows/pr-preview.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ jobs:
7676
ref: ${{ github.event.pull_request.head.sha }}
7777
path: pr
7878
submodules: recursive
79+
fetch-depth: 0
7980
- name: Install Node.js dependencies for PR
8081
working-directory: pr
8182
run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
@@ -93,6 +94,14 @@ jobs:
9394
--baseURL "${{ steps.base_url.outputs.base_url }}/" \
9495
--destination "${{ github.workspace }}/public/pr-${{ github.event.number }}"
9596
97+
- name: Check internal links
98+
working-directory: pr
99+
env:
100+
SITE_BASE_URL: ${{ steps.base_url.outputs.base_url }}/
101+
PUBLIC_DIR: ${{ github.workspace }}/public/pr-${{ github.event.number }}
102+
LINK_CHECK_BASE_REF: origin/${{ github.base_ref }}
103+
run: node scripts/check-changed-links.mjs
104+
96105
- name: Upload artifact
97106
uses: actions/upload-pages-artifact@v3
98107
with:

.github/workflows/production-deploy.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ jobs:
4848
uses: actions/checkout@v4
4949
with:
5050
submodules: recursive
51+
fetch-depth: 0
5152
- name: Setup Pages
5253
id: pages
5354
uses: actions/configure-pages@v5
@@ -63,6 +64,11 @@ jobs:
6364
--minify \
6465
--baseURL "${{ steps.pages.outputs.base_url }}/"
6566
67+
- name: Check internal links
68+
env:
69+
SITE_BASE_URL: ${{ steps.pages.outputs.base_url }}/
70+
run: node scripts/check-changed-links.mjs
71+
6672
- name: Upload artifact
6773
uses: actions/upload-pages-artifact@v3
6874
with:

config.toml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,22 +316,28 @@ pluralizelisttitles = false
316316
weight = 2
317317
parent = "menu.programs_activities"
318318

319+
[[menu.main]]
320+
name = "RISE"
321+
url = "/rise/rise"
322+
weight = 3
323+
parent = "menu.programs_activities"
324+
319325
[[menu.main]]
320326
name = "Travel Support and Childcare Assustance (CAPS)"
321327
url = "/activities/capsmain"
322-
weight = 3
328+
weight = 4
323329
parent = "menu.programs_activities"
324330

325331
[[menu.main]]
326332
name = "Webinars"
327333
url = "/activities/webinarsmain"
328-
weight = 4
334+
weight = 5
329335
parent = "menu.programs_activities"
330336

331337
[[menu.main]]
332338
name = "Summer/Winter Schools"
333339
url = "/activities/schools"
334-
weight = 5
340+
weight = 6
335341
parent = "menu.programs_activities"
336342

337343
[[menu.main]]

content/activities/overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ ACM SIGSOFT sponsors many activities and events.
99
Here is a non-exhaustive list of them.
1010

1111
- [CARES]({{< ref "/cares/SIGSOFT_CARES.md" >}} "cares")
12+
- [RISE: Research Alliance for Industry and Academia in Software Engineering]({{< ref "/rise/RISE.md" >}} "rise")
1213
- [SIGSOFT Travel Support and Childcare Assistance at Conferences - CAPS]({{< ref "CAPSMAIN.md" >}} "caps")
1314
- [WEBINARS]({{< ref "webinarsmain.md" >}} "webinars")
1415
- [Summer/Winter Schools]({{< ref "schools.md" >}} "schools")
@@ -21,4 +22,3 @@ Here is a list of new initiatives that SIGSOFT is exploring, and the correspondi
2122
- SIGSOFT ACM Digital Library liaisons: Xiaoning Du and Tao Zhang. The liaisons will ensure that information about SIGSOFT (co)-located events has been correctly captured in ACM Digital Library.
2223
- SIGSOFT Conference Program Committee Member Recognition Coordinators: Sylvain Hallé and Yepang Liu. The coordinators will create and mail personalized digital certificates for program committee roles of SIGSOFT (co)-sponsored conferences.
2324
- Research Highlights coordinator: Silvia Abrahão. The coordinator will work with program chairs of SIGSOFT (co)-sponsored conferences to identify most promising papers for possible publication in Communications of the ACM.
24-

content/rise/RISE.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
+++
2+
title = "RISE: Research Alliance for Industry and Academia in Software Engineering"
3+
description = "An ACM SIGSOFT Task Force on AI and Software Engineering in India"
4+
keywords = ["RISE", "AI and Software Engineering", "India", "Task Force"]
5+
+++
6+
7+
RISE (Research Alliance for Industry and Academia in Software Engineering) is an ACM SIGSOFT Task Force and a research-driven initiative that fosters engagement between Indian software industry, government, and the global software engineering research community. Its current focus is on AI and Software Engineering in India.
8+
9+
The RISE website is available at [https://rise-sigsoft.github.io/](https://rise-sigsoft.github.io/).
10+
11+
## Purpose
12+
13+
India hosts one of the world's largest software engineering ecosystems, employing over five million software professionals across diverse domains and cities, with major global R&D centres operated by Microsoft, IBM, Mercedes-Benz, Shell, SAP, and others. Yet despite accounting for around 16% of the global software workforce, India's representation in top-tier international software engineering venues such as ICSE, FSE, and ASE remains below 2%.
14+
15+
As AI-driven software development accelerates, India's industry is witnessing large-scale adoption of generative and predictive AI in everyday software work. This presents an opportunity to document, understand, and shape how AI is transforming software engineering practice in real-world industrial contexts.
16+
17+
Operating under ACM SIGSOFT, RISE works to surface real-world AI and software engineering challenges from Indian industry, mobilise academic researchers to engage with them, and build structured linkages with industry bodies such as NASSCOM and adjacent platforms across industry and government.
18+
19+
## Mission
20+
21+
The Task Force is organised around four working pillars.
22+
23+
**Industry Engagement and Challenge Discovery.** Reach out to major software R&D centres, software powerhouses, and startups across India to identify and document real-world AI-driven software engineering practices, challenges, and emerging solutions through structured meetings and industry events.
24+
25+
**Industry Body Integration.** Build formal collaboration between ACM SIGSOFT and Indian software industry bodies such as NASSCOM, with the goal of creating dedicated threads on Software Engineering and Research toward Indian and global software engineering research.
26+
27+
**Co-designed Research Challenges.** Mobilise academic researchers to engage with industry-defined problems through co-created research challenge calls, pilot projects, and a curated catalogue of challenges accessible to the SIGSOFT community.
28+
29+
**Industry Consortium and Sustainability.** Build an industry consortium across associations, R&D centres, and product firms to sustain RISE activities, with the aim of industry-led continuity integrated into ISEC and adjacent forums over time.
30+
31+
## Ecosystem
32+
33+
RISE works across a broad set of stakeholders that shape software engineering practice and research in India. These span industry and R&D centres, industry bodies and associations, government and standards bodies, and academic and industry conference venues. A working stakeholder map is maintained by the Task Force and will be expanded as it progresses.
34+
35+
## Leadership
36+
37+
RISE is chaired by Sridhar Chimalakonda, Associate Professor and Head of the Department of Computer Science and Engineering at IIT Tirupati, Adjunct Associate Professor at the University of Waterloo, and Digital Learning Co-Chair of ACM SIGSOFT. Co-leads from industry and academia will be identified and confirmed in due course.
38+
39+
RISE is established as an ACM SIGSOFT Task Force, with annual reporting to the SIGSOFT Executive Committee.
40+
41+
## Get Involved
42+
43+
RISE welcomes researchers, practitioners, students, and chapter representatives who would like to take part. You can express your interest in participating through the form on the RISE website:
44+
45+
[Visit the RISE website](https://rise-sigsoft.github.io/)
46+
47+
## Contact
48+
49+
Sridhar Chimalakonda, Chair, RISE Task Force
50+
Department of Computer Science and Engineering, IIT Tirupati
51+
Email: [ch@iittp.ac.in](mailto:ch@iittp.ac.in)

scripts/check-changed-links.mjs

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
#!/usr/bin/env node
2+
3+
import fs from "node:fs";
4+
import path from "node:path";
5+
import { execFileSync } from "node:child_process";
6+
7+
const publicRoot = path.resolve(process.env.PUBLIC_DIR || "public");
8+
const siteBase = new URL(process.env.SITE_BASE_URL || "https://www2.sigsoft.org/");
9+
const siteBasePath = normalizePath(siteBase.pathname);
10+
const ignoredProtocols = new Set(["mailto:", "tel:", "javascript:", "data:", "blob:"]);
11+
12+
const changedFiles = process.argv.slice(2).filter((file) => fs.existsSync(file));
13+
const filesToCheck = changedFiles.length > 0 ? changedFiles : gitChangedFiles();
14+
const failures = [];
15+
16+
for (const file of filesToCheck) {
17+
if (!/\.(md|markdown|html|toml|yaml|yml)$/i.test(file)) continue;
18+
19+
const source = fs.readFileSync(file, "utf8");
20+
for (const link of extractLinks(source)) {
21+
checkLink(file, link);
22+
}
23+
}
24+
25+
if (failures.length > 0) {
26+
console.error(`Found ${failures.length} broken link(s) in changed files:`);
27+
for (const failure of failures) console.error(`- ${failure}`);
28+
process.exit(1);
29+
}
30+
31+
console.log(`Checked links in ${filesToCheck.length} changed file(s); no new broken internal links found.`);
32+
33+
function checkLink(sourceFile, rawLink) {
34+
const link = decodeHtml(rawLink.trim());
35+
if (!link || link.startsWith("#") || link.startsWith("//")) return;
36+
37+
if (link.includes("{{<") || link.includes("{{%")) return;
38+
39+
const refTarget = parseHugoRef(link);
40+
if (refTarget) {
41+
checkContentPath(sourceFile, link, refTarget);
42+
return;
43+
}
44+
45+
if (/\.md$/i.test(link)) {
46+
checkContentPath(sourceFile, link, link);
47+
return;
48+
}
49+
50+
const internalPath = toInternalPath(link);
51+
if (!internalPath) return;
52+
53+
const target = resolvePublicPath(internalPath);
54+
if (!fs.existsSync(target)) {
55+
failures.push(`${sourceFile}: broken internal link ${link} -> ${slash(path.relative(publicRoot, target))}`);
56+
}
57+
}
58+
59+
function checkContentPath(sourceFile, link, target) {
60+
const contentPath = target.startsWith("/")
61+
? path.resolve("content", target.replace(/^\/+/, ""))
62+
: path.resolve(path.dirname(sourceFile), target);
63+
64+
if (!fs.existsSync(contentPath)) {
65+
failures.push(`${sourceFile}: broken Hugo/content ref ${link} -> ${slash(path.relative(process.cwd(), contentPath))}`);
66+
}
67+
}
68+
69+
function extractLinks(source) {
70+
const links = new Set();
71+
const patterns = [
72+
/\[[^\]]*]\(([^)\s]+)(?:\s+["'][^"']*["'])?\)/g,
73+
/\b(?:href|src|action)=["']([^"']+)["']/gi,
74+
/^\s*url\s*=\s*["']([^"']+)["']/gim,
75+
/\{\{<\s*ref\s+["']([^"']+)["']\s*>}}/g,
76+
/\{\{%\s*ref\s+["']([^"']+)["']\s*%}}/g,
77+
];
78+
79+
for (const pattern of patterns) {
80+
let match;
81+
while ((match = pattern.exec(source)) !== null) {
82+
links.add(match[1]);
83+
}
84+
}
85+
86+
return links;
87+
}
88+
89+
function parseHugoRef(link) {
90+
const match = link.match(/^\{\{[<%]\s*ref\s+["']([^"']+)["']\s*[>%]}}$/);
91+
return match ? match[1] : null;
92+
}
93+
94+
function toInternalPath(link) {
95+
let value = link;
96+
97+
try {
98+
const url = new URL(link);
99+
if (ignoredProtocols.has(url.protocol)) return null;
100+
if (url.origin !== siteBase.origin) return null;
101+
value = stripSiteBasePath(url.pathname);
102+
} catch {
103+
if (/^[a-z][a-z0-9+.-]*:/i.test(link)) return null;
104+
if (!link.startsWith("/")) return null;
105+
value = link;
106+
}
107+
108+
value = value.split("#")[0].split("?")[0];
109+
return value || "/";
110+
}
111+
112+
function resolvePublicPath(urlPath) {
113+
const decoded = safeDecode(urlPath).replace(/^\/+/, "");
114+
const candidate = path.join(publicRoot, decoded);
115+
116+
if (path.extname(candidate)) return candidate;
117+
if (urlPath.endsWith("/")) return path.join(candidate, "index.html");
118+
119+
const asDirectoryIndex = path.join(candidate, "index.html");
120+
if (fs.existsSync(asDirectoryIndex)) return asDirectoryIndex;
121+
return `${candidate}.html`;
122+
}
123+
124+
function gitChangedFiles() {
125+
const baseRef = process.env.LINK_CHECK_BASE_REF;
126+
const args = baseRef ? ["diff", "--name-only", `${baseRef}...HEAD`] : ["diff", "--name-only", "HEAD^", "HEAD"];
127+
128+
try {
129+
const files = execFileSync("git", args, { encoding: "utf8" })
130+
.split(/\r?\n/)
131+
.filter(Boolean);
132+
133+
if (files.length === 0) {
134+
console.error(`No changed files found from: git ${args.join(" ")}`);
135+
console.error("Pass files explicitly or ensure the checkout has enough git history.");
136+
process.exit(1);
137+
}
138+
139+
return files;
140+
} catch {
141+
console.error(`Unable to determine changed files from: git ${args.join(" ")}`);
142+
console.error("Pass files explicitly or ensure the checkout has enough git history.");
143+
process.exit(1);
144+
}
145+
}
146+
147+
function stripSiteBasePath(urlPath) {
148+
const normalized = normalizePath(urlPath);
149+
if (siteBasePath !== "/" && normalized.startsWith(siteBasePath + "/")) {
150+
return normalized.slice(siteBasePath.length);
151+
}
152+
return normalized;
153+
}
154+
155+
function normalizePath(value) {
156+
const normalized = value.startsWith("/") ? value : `/${value}`;
157+
return normalized.length > 1 ? normalized.replace(/\/+$/, "") : normalized;
158+
}
159+
160+
function safeDecode(value) {
161+
try {
162+
return decodeURIComponent(value);
163+
} catch {
164+
return value;
165+
}
166+
}
167+
168+
function decodeHtml(value) {
169+
return value
170+
.replaceAll("&amp;", "&")
171+
.replaceAll("&quot;", '"')
172+
.replaceAll("&#34;", '"')
173+
.replaceAll("&#39;", "'")
174+
.replaceAll("&apos;", "'")
175+
.replaceAll("&lt;", "<")
176+
.replaceAll("&gt;", ">");
177+
}
178+
179+
function slash(value) {
180+
return value.split(path.sep).join("/");
181+
}

0 commit comments

Comments
 (0)