From dc5e97efee812256cfe1905d7cae7dce12f2d111 Mon Sep 17 00:00:00 2001 From: yqtian Date: Sat, 23 May 2026 21:40:22 +1000 Subject: [PATCH 1/3] Add RISE program page --- .github/workflows/pr-preview.yml | 9 ++ .github/workflows/production-deploy.yml | 5 + config.toml | 12 +- content/activities/overview.md | 2 +- content/rise/RISE.md | 51 +++++++ scripts/check-changed-links.mjs | 171 ++++++++++++++++++++++++ 6 files changed, 246 insertions(+), 4 deletions(-) create mode 100644 content/rise/RISE.md create mode 100644 scripts/check-changed-links.mjs diff --git a/.github/workflows/pr-preview.yml b/.github/workflows/pr-preview.yml index f360cbc..d863753 100644 --- a/.github/workflows/pr-preview.yml +++ b/.github/workflows/pr-preview.yml @@ -76,6 +76,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} path: pr submodules: recursive + fetch-depth: 0 - name: Install Node.js dependencies for PR working-directory: pr run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true" @@ -93,6 +94,14 @@ jobs: --baseURL "${{ steps.base_url.outputs.base_url }}/" \ --destination "${{ github.workspace }}/public/pr-${{ github.event.number }}" + - name: Check internal links + working-directory: pr + env: + SITE_BASE_URL: ${{ steps.base_url.outputs.base_url }}/ + PUBLIC_DIR: ${{ github.workspace }}/public/pr-${{ github.event.number }} + LINK_CHECK_BASE_REF: origin/${{ github.base_ref }} + run: node scripts/check-changed-links.mjs + - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: diff --git a/.github/workflows/production-deploy.yml b/.github/workflows/production-deploy.yml index 89702c0..f3247f2 100644 --- a/.github/workflows/production-deploy.yml +++ b/.github/workflows/production-deploy.yml @@ -63,6 +63,11 @@ jobs: --minify \ --baseURL "${{ steps.pages.outputs.base_url }}/" + - name: Check internal links + env: + SITE_BASE_URL: ${{ steps.pages.outputs.base_url }}/ + run: node scripts/check-changed-links.mjs + - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: diff --git a/config.toml b/config.toml index 12f9c5c..6a2a48f 100644 --- a/config.toml +++ b/config.toml @@ -316,22 +316,28 @@ pluralizelisttitles = false weight = 2 parent = "menu.programs_activities" +[[menu.main]] + name = "RISE" + url = "/rise/rise" + weight = 3 + parent = "menu.programs_activities" + [[menu.main]] name = "Travel Support and Childcare Assustance (CAPS)" url = "/activities/capsmain" - weight = 3 + weight = 4 parent = "menu.programs_activities" [[menu.main]] name = "Webinars" url = "/activities/webinarsmain" - weight = 4 + weight = 5 parent = "menu.programs_activities" [[menu.main]] name = "Summer/Winter Schools" url = "/activities/schools" - weight = 5 + weight = 6 parent = "menu.programs_activities" [[menu.main]] diff --git a/content/activities/overview.md b/content/activities/overview.md index c2549a5..1374821 100644 --- a/content/activities/overview.md +++ b/content/activities/overview.md @@ -9,6 +9,7 @@ ACM SIGSOFT sponsors many activities and events. Here is a non-exhaustive list of them. - [CARES]({{< ref "/cares/SIGSOFT_CARES.md" >}} "cares") +- [RISE: Research Alliance for Industry and Academia in Software Engineering]({{< ref "/rise/RISE.md" >}} "rise") - [SIGSOFT Travel Support and Childcare Assistance at Conferences - CAPS]({{< ref "CAPSMAIN.md" >}} "caps") - [WEBINARS]({{< ref "webinarsmain.md" >}} "webinars") - [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 - 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. - 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. - 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. - diff --git a/content/rise/RISE.md b/content/rise/RISE.md new file mode 100644 index 0000000..46208de --- /dev/null +++ b/content/rise/RISE.md @@ -0,0 +1,51 @@ ++++ +title = "RISE: Research Alliance for Industry and Academia in Software Engineering" +description = "An ACM SIGSOFT Task Force on AI and Software Engineering in India" +keywords = ["RISE", "AI and Software Engineering", "India", "Task Force"] ++++ + +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. + +The RISE website is available at [https://rise-sigsoft.github.io/](https://rise-sigsoft.github.io/). + +## Purpose + +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%. + +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. + +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. + +## Mission + +The Task Force is organised around four working pillars. + +**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. + +**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. + +**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. + +**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. + +## Ecosystem + +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. + +## Leadership + +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. + +RISE is established as an ACM SIGSOFT Task Force, with annual reporting to the SIGSOFT Executive Committee. + +## Get Involved + +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: + +[Visit the RISE website](https://rise-sigsoft.github.io/) + +## Contact + +Sridhar Chimalakonda, Chair, RISE Task Force +Department of Computer Science and Engineering, IIT Tirupati +Email: [ch@iittp.ac.in](mailto:ch@iittp.ac.in) diff --git a/scripts/check-changed-links.mjs b/scripts/check-changed-links.mjs new file mode 100644 index 0000000..45e613e --- /dev/null +++ b/scripts/check-changed-links.mjs @@ -0,0 +1,171 @@ +#!/usr/bin/env node + +import fs from "node:fs"; +import path from "node:path"; +import { execFileSync } from "node:child_process"; + +const publicRoot = path.resolve(process.env.PUBLIC_DIR || "public"); +const siteBase = new URL(process.env.SITE_BASE_URL || "https://www2.sigsoft.org/"); +const siteBasePath = normalizePath(siteBase.pathname); +const ignoredProtocols = new Set(["mailto:", "tel:", "javascript:", "data:", "blob:"]); + +const changedFiles = process.argv.slice(2).filter((file) => fs.existsSync(file)); +const filesToCheck = changedFiles.length > 0 ? changedFiles : gitChangedFiles(); +const failures = []; + +for (const file of filesToCheck) { + if (!/\.(md|markdown|html|toml|yaml|yml)$/i.test(file)) continue; + + const source = fs.readFileSync(file, "utf8"); + for (const link of extractLinks(source)) { + checkLink(file, link); + } +} + +if (failures.length > 0) { + console.error(`Found ${failures.length} broken link(s) in changed files:`); + for (const failure of failures) console.error(`- ${failure}`); + process.exit(1); +} + +console.log(`Checked links in ${filesToCheck.length} changed file(s); no new broken internal links found.`); + +function checkLink(sourceFile, rawLink) { + const link = decodeHtml(rawLink.trim()); + if (!link || link.startsWith("#") || link.startsWith("//")) return; + + if (link.includes("{{<") || link.includes("{{%")) return; + + const refTarget = parseHugoRef(link); + if (refTarget) { + checkContentPath(sourceFile, link, refTarget); + return; + } + + if (/\.md$/i.test(link)) { + checkContentPath(sourceFile, link, link); + return; + } + + const internalPath = toInternalPath(link); + if (!internalPath) return; + + const target = resolvePublicPath(internalPath); + if (!fs.existsSync(target)) { + failures.push(`${sourceFile}: broken internal link ${link} -> ${slash(path.relative(publicRoot, target))}`); + } +} + +function checkContentPath(sourceFile, link, target) { + const contentPath = target.startsWith("/") + ? path.resolve("content", target.replace(/^\/+/, "")) + : path.resolve(path.dirname(sourceFile), target); + + if (!fs.existsSync(contentPath)) { + failures.push(`${sourceFile}: broken Hugo/content ref ${link} -> ${slash(path.relative(process.cwd(), contentPath))}`); + } +} + +function extractLinks(source) { + const links = new Set(); + const patterns = [ + /\[[^\]]*]\(([^)\s]+)(?:\s+["'][^"']*["'])?\)/g, + /\b(?:href|src|action)=["']([^"']+)["']/gi, + /^\s*url\s*=\s*["']([^"']+)["']/gim, + /\{\{<\s*ref\s+["']([^"']+)["']\s*>}}/g, + /\{\{%\s*ref\s+["']([^"']+)["']\s*%}}/g, + ]; + + for (const pattern of patterns) { + let match; + while ((match = pattern.exec(source)) !== null) { + links.add(match[1]); + } + } + + return links; +} + +function parseHugoRef(link) { + const match = link.match(/^\{\{[<%]\s*ref\s+["']([^"']+)["']\s*[>%]}}$/); + return match ? match[1] : null; +} + +function toInternalPath(link) { + let value = link; + + try { + const url = new URL(link); + if (ignoredProtocols.has(url.protocol)) return null; + if (url.origin !== siteBase.origin) return null; + value = stripSiteBasePath(url.pathname); + } catch { + if (/^[a-z][a-z0-9+.-]*:/i.test(link)) return null; + if (!link.startsWith("/")) return null; + value = link; + } + + value = value.split("#")[0].split("?")[0]; + return value || "/"; +} + +function resolvePublicPath(urlPath) { + const decoded = safeDecode(urlPath).replace(/^\/+/, ""); + const candidate = path.join(publicRoot, decoded); + + if (path.extname(candidate)) return candidate; + if (urlPath.endsWith("/")) return path.join(candidate, "index.html"); + + const asDirectoryIndex = path.join(candidate, "index.html"); + if (fs.existsSync(asDirectoryIndex)) return asDirectoryIndex; + return `${candidate}.html`; +} + +function gitChangedFiles() { + const baseRef = process.env.LINK_CHECK_BASE_REF; + const args = baseRef ? ["diff", "--name-only", `${baseRef}...HEAD`] : ["diff", "--name-only", "HEAD^", "HEAD"]; + + try { + return execFileSync("git", args, { encoding: "utf8" }) + .split(/\r?\n/) + .filter(Boolean); + } catch { + return []; + } +} + +function stripSiteBasePath(urlPath) { + const normalized = normalizePath(urlPath); + if (siteBasePath !== "/" && normalized.startsWith(siteBasePath + "/")) { + return normalized.slice(siteBasePath.length); + } + return normalized; +} + +function normalizePath(value) { + const normalized = value.startsWith("/") ? value : `/${value}`; + return normalized.length > 1 ? normalized.replace(/\/+$/, "") : normalized; +} + +function safeDecode(value) { + try { + return decodeURIComponent(value); + } catch { + return value; + } +} + +function decodeHtml(value) { + return value + .replaceAll("&", "&") + .replaceAll(""", '"') + .replaceAll(""", '"') + .replaceAll("'", "'") + .replaceAll("'", "'") + .replaceAll("<", "<") + .replaceAll(">", ">"); +} + +function slash(value) { + return value.split(path.sep).join("/"); +} From b4b20809528b87374c6d21842ab44b2c637cf6b5 Mon Sep 17 00:00:00 2001 From: yqtian Date: Sat, 23 May 2026 21:50:31 +1000 Subject: [PATCH 2/3] Harden changed link check --- .github/workflows/production-deploy.yml | 1 + scripts/check-changed-links.mjs | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/production-deploy.yml b/.github/workflows/production-deploy.yml index f3247f2..7635751 100644 --- a/.github/workflows/production-deploy.yml +++ b/.github/workflows/production-deploy.yml @@ -48,6 +48,7 @@ jobs: uses: actions/checkout@v4 with: submodules: recursive + fetch-depth: 0 - name: Setup Pages id: pages uses: actions/configure-pages@v5 diff --git a/scripts/check-changed-links.mjs b/scripts/check-changed-links.mjs index 45e613e..e930150 100644 --- a/scripts/check-changed-links.mjs +++ b/scripts/check-changed-links.mjs @@ -126,11 +126,21 @@ function gitChangedFiles() { const args = baseRef ? ["diff", "--name-only", `${baseRef}...HEAD`] : ["diff", "--name-only", "HEAD^", "HEAD"]; try { - return execFileSync("git", args, { encoding: "utf8" }) + const files = execFileSync("git", args, { encoding: "utf8" }) .split(/\r?\n/) .filter(Boolean); + + if (files.length === 0) { + console.error(`No changed files found from: git ${args.join(" ")}`); + console.error("Pass files explicitly or ensure the checkout has enough git history."); + process.exit(1); + } + + return files; } catch { - return []; + console.error(`Unable to determine changed files from: git ${args.join(" ")}`); + console.error("Pass files explicitly or ensure the checkout has enough git history."); + process.exit(1); } } From 2bde66487bd36ba4c26eef6b35cc245ab566d51b Mon Sep 17 00:00:00 2001 From: yqtian Date: Tue, 26 May 2026 11:30:58 +1000 Subject: [PATCH 3/3] Fix menu link casing --- config.toml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/config.toml b/config.toml index 6a2a48f..4b694fb 100644 --- a/config.toml +++ b/config.toml @@ -74,7 +74,7 @@ pluralizelisttitles = false [[menu.main]] name = "Program Committee Policy" - url = "/policies/PCpolicy" + url = "/policies/pcpolicy" weight = 2 parent = "menu.conferences" @@ -136,7 +136,7 @@ pluralizelisttitles = false [[menu.main]] name = "Software Engineering Notes (SEN)" - url = "/SEN/SEN" + url = "/sen/sen" weight = 3 parent = "menu.pubs" @@ -223,25 +223,25 @@ pluralizelisttitles = false [[menu.main]] name = "Distinguished Service" - url = "/awards/distinguishedService" + url = "/awards/distinguishedservice" weight = 2 parent = "section.awards" [[menu.main]] name = "Outstanding Research" - url = "/awards/outstandingResearch" + url = "/awards/outstandingresearch" weight = 3 parent = "section.awards" [[menu.main]] name = "Influential Educator" - url = "/awards/influentialEducator" + url = "/awards/influentialeducator" weight = 4 parent = "section.awards" [[menu.main]] name = "Impact Paper" - url = "/awards/impactPaper" + url = "/awards/impactpaper" weight = 5 parent = "section.awards" @@ -253,7 +253,7 @@ pluralizelisttitles = false [[menu.main]] name = "Early Career Researcher" - url = "/awards/earlyCareerResearcher" + url = "/awards/earlycareerresearcher" weight = 7 parent = "section.awards" @@ -271,7 +271,7 @@ pluralizelisttitles = false [[menu.main]] name = "ACM SIGSOFT Distinguished Paper" - url = "/awards/distinguishedPaper" + url = "/awards/distinguishedpaper" weight = 1 parent = "section.conference"