diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml new file mode 100644 index 0000000..f2a6c33 --- /dev/null +++ b/.github/workflows/check-links.yml @@ -0,0 +1,91 @@ +name: Check links + +on: + pull_request: + push: + branches: [main] + paths: [links.js] + schedule: + # Weekly check of every link for ones that have gone stale (Monday, 9am aest/aedt) + - cron: '0 23 * * 0' + workflow_dispatch: + +jobs: + lint: + if: github.event_name != 'schedule' + runs-on: ubuntu-latest + permissions: + contents: write # push formatting fixes + pages: write # rebuild the site after fixing main + steps: + - uses: actions/checkout@v4 + with: + # PR branch so fixes can be pushed back to it + ref: ${{ github.head_ref || github.ref }} + # Optional PAT so fix commits re-trigger checks; falls back to the default token + token: ${{ secrets.LINKS_BOT_TOKEN || github.token }} + + - uses: actions/setup-node@v4 + with: + node-version: 22 + + # The most common break from browser-based edits: a missing or extra comma + # between entries. Formatters can't fix this, so janky insert commas where an + # entry/field ends and the next line starts a new key, and collapse doubled commas. + - name: Repair commas + run: | + if ! node --check links.js 2>/dev/null; then + perl -0pi -e "s/([}'\"])([ \t]*)(\r?\n\s*(?:['\"][^'\"\n]+['\"]|url|description)\s*:)/\$1,\$2\$3/g; s/,(\s*\n\s*),/,\$1/g" links.js + fi + + - name: Format + run: npx --yes prettier@3 --write links.js + + - name: Lint + run: npx --yes eslint@10 links.js + + - name: Commit fixes + id: commit + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + run: | + if git diff --quiet links.js; then exit 0; fi + git config user.name "github-actions[bot]" + git config user.email "41898283+github-actions[bot]@users.noreply.github.com" + git commit -m "Auto-fix links.js formatting" links.js + git push + echo "pushed=true" >> "$GITHUB_OUTPUT" + + - name: Rebuild site + if: steps.commit.outputs.pushed == 'true' && github.event_name == 'push' + run: gh api -X POST "repos/${{ github.repository }}/pages/builds" + env: + GH_TOKEN: ${{ github.token }} + + - name: Fail if fork PR needs fixes + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository + run: git diff --exit-code links.js + + links: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # On PRs/pushes only check lines that were added, so an unrelated dead + # link doesn't block someone adding theirs. Scheduled runs check everything. + - name: Pick what to check + id: target + run: | + case "${{ github.event_name }}" in + pull_request) base="origin/${{ github.base_ref }}" ;; + push) base="${{ github.event.before }}" ;; + *) echo "input=links.js" >> "$GITHUB_OUTPUT"; exit 0 ;; + esac + git diff "$base" -- links.js | grep '^+' | grep -v '^+++' > added-lines.txt || true + echo "input=added-lines.txt" >> "$GITHUB_OUTPUT" + + - uses: lycheeverse/lychee-action@v2 + with: + args: --config lychee.toml --no-progress ${{ steps.target.outputs.input }} + fail: true diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..f2c3824 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,5 @@ +{ + "singleQuote": true, + "quoteProps": "preserve", + "printWidth": 1000 +} diff --git a/README.md b/README.md index 9a5e26e..e4cf4a1 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,8 @@ Edit [links.js](links.js) and add an entry to the `REDIRECTS` object: The shortlink will be live at `sih.tools/shortcut` as soon as the change is merged. > **Note:** Every entry in the object must be followed by a comma, including the one before your new entry. Missing commas will break all redirects. +> +> A GitHub Action checks every change to `links.js`: it adds missing commas, fixes formatting (committing the fix back to your branch), flags duplicate shortlinks, and checks that new URLs aren't broken. If a check fails, see the **Actions** tab or the PR's checks for the line to fix. Shortlinks can be hierarchical — slashes in the key create nested URLs: diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..d738b25 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,11 @@ +// Lints links.js only (checked in CI by .github/workflows/check-links.yml) +export default [ + { + files: ['links.js'], + languageOptions: { sourceType: 'script', ecmaVersion: 'latest' }, + rules: { + 'no-dupe-keys': 'error', + 'comma-dangle': 'off', + }, + }, +]; diff --git a/links.js b/links.js index b30ee4d..b771d9f 100644 --- a/links.js +++ b/links.js @@ -2,155 +2,155 @@ const REDIRECTS = { 'aigis': { url: 'https://github.com/Sydney-Informatics-Hub/aigis/blob/main/README.md', - description: 'Geospatial data with Gen-AI - Sydney Informatics Hub' + description: 'Geospatial data with Gen-AI - Sydney Informatics Hub', }, 'alphafold3': { url: 'https://sydney-informatics-hub.github.io/gpu-cluster-onboarding-guide/notebooks/alphafold3_guide.html', - description: 'Guide to run AlphaFold3 on the Apollo GPU cluster' + description: 'Guide to run AlphaFold3 on the Apollo GPU cluster', }, 'apollo-launch': { url: 'https://sydney-informatics-hub.github.io/apollo-launch/', - description: 'Program for the official launch event of the Apollo GPU cluster on 3rd September 2026' + description: 'Program for the official launch event of the Apollo GPU cluster on 3rd September 2026', }, 'appn': { url: 'https://www.plantphenomics.org.au/our-nodes/university-of-sydney', - description: 'University of Sydney node of the Australian Plant Phenomics Facility - Sydney Informatics Hub' + description: 'University of Sydney node of the Australian Plant Phenomics Facility - Sydney Informatics Hub', }, 'biocommons-access': { url: 'https://www.biocommons.org.au/access', - description: 'Biocommons Access service developed by the BioCloud team at Sydney Informatics Hub' + description: 'Biocommons Access service developed by the BioCloud team at Sydney Informatics Hub', }, 'bioshell-guide': { url: 'https://sydney-informatics-hub.github.io/BioShell-User-Guide/', - description: 'Documentation to request access and use BioShell - A command-line environment built for life sciences research' + description: 'Documentation to request access and use BioShell - A command-line environment built for life sciences research', }, 'bio': { url: 'https://github.com/Sydney-Informatics-Hub/Bioinformatics', - description: 'SIH bioinformatics resources and tools' + description: 'SIH bioinformatics resources and tools', }, 'bio-omicscalculator': { url: 'https://sydney-informatics-hub.github.io/omics-calculator/metadata-calculator.html', - description: 'Bioinformatics metadata analysis tool' + description: 'Bioinformatics metadata analysis tool', }, 'bio-training': { url: 'https://sydney-informatics-hub.github.io/bioinformatics-training/', - description: 'SIH bioinformatics training' + description: 'SIH bioinformatics training', }, 'bio101-workshop': { url: 'https://sydney-informatics-hub.github.io/bio101-workshop/', - description: 'Bioinformatics 101 workshop materials' + description: 'Bioinformatics 101 workshop materials', }, 'bio101-survey-2026': { url: 'https://redcap.sydney.edu.au/surveys/?s=FJ33MYNCRR&training=106&training_date=2026-09-17', - description: 'Bioinformatics 101 workshop survey' + description: 'Bioinformatics 101 workshop survey', }, 'blockparty': { url: 'https://sydney-informatics-hub.github.io/blockparty/', - description: 'Unicode drawing tool' + description: 'Unicode drawing tool', }, 'chat': { url: 'https://rds-core-sih4hpc-rw-openwebui.gpu.sydney.edu.au', - description: 'Internal OpenWebUI chat server (needs VPN to access)' + description: 'Internal OpenWebUI chat server (needs VPN to access)', }, 'CBI-R': { url: 'https://sydney-informatics-hub.github.io/cbir-screening-tool/', - description: 'Online Screening Tool for the Cambridge Behavioural Inventory-Revised (CBI-R)' + description: 'Online Screening Tool for the Cambridge Behavioural Inventory-Revised (CBI-R)', }, 'client-dashboard': { url: 'https://app.powerbi.com/groups/6d62871b-a2c5-4c79-bb85-75eb03e90a2d/reports/700a9b42-6a01-44ce-ac7f-a7e0eeeffc35?ctid=82b3e37e-8171-485d-b10b-38dae7ed14a8', - description: 'SIH Client Dashboard' + description: 'SIH Client Dashboard', }, 'nhmrc-grants-2026': { url: 'https://sydney.au1.qualtrics.com/jfe/form/SV_6lFIfPlKRKplg3A', - description: 'USyd Collaborative Equipment Grants and NHMRC Equipment Grants Scheme' + description: 'USyd Collaborative Equipment Grants and NHMRC Equipment Grants Scheme', }, 'cc': { url: 'https://ardc.edu.au/project/curated-collections-for-enduring-hass-indigenous-data/', - description: 'Curated Collections, a national digital humanities collection building platform based upon Omeka S, developed by Sydney Informatics Hub in partnership with the ARDC, Systemik and USyd FASS.' - }, + description: 'Curated Collections, a national digital humanities collection building platform based upon Omeka S, developed by Sydney Informatics Hub in partnership with the ARDC, Systemik and USyd FASS.', + }, 'forwards-plan': { url: 'https://unisyd.sharepoint.com/:x:/r/teams/SydneyInformaticsHub2/Shared Documents/1 SIH Central Document Repository/Team Folders/Informatics/Group Leads/Team management/TeamForwardPlanning2025onwards.xlsx?web=1', - description: 'SIH Informatics Team Forwards Plan' + description: 'SIH Informatics Team Forwards Plan', }, 'github-external': { url: 'https://github.com/Sydney-Informatics-Hub', - description: 'Public SIH GitHub organization' + description: 'Public SIH GitHub organization', }, 'github-internal': { url: 'https://github.sydney.edu.au/informatics', - description: 'Internal SIH GitHub organization' + description: 'Internal SIH GitHub organization', }, 'globus/docs': { url: 'https://sydneyuni.atlassian.net/wiki/spaces/RC/pages/4392878084/Globus', - description: 'Globus technical documentation on confluence' + description: 'Globus technical documentation on confluence', }, 'gpu/docs': { url: 'https://sydneyuni.atlassian.net/wiki/x/AYBd1Q', - description: 'Apollo GPU cluster documentation, information and specifications' + description: 'Apollo GPU cluster documentation, information and specifications', }, 'gpu/pricing': { url: 'https://sydneyuni.atlassian.net/wiki/x/DYBgGgE', - description: 'Apollo GPU cluster pricing, rates and billing' + description: 'Apollo GPU cluster pricing, rates and billing', }, 'gpu/policies': { url: 'https://sydneyuni.atlassian.net/wiki/x/mwWHEgE', - description: 'Apollo GPU cluster access policies' + description: 'Apollo GPU cluster access policies', }, 'gpu/onboarding': { url: 'https://dashr.sydney.edu.au', - description: 'Apollo GPU cluster onboarding for your research project in DashR portal' + description: 'Apollo GPU cluster onboarding for your research project in DashR portal', }, 'gpu/login': { url: 'https://gpu.sydney.edu.au', - description: 'Apollo GPU cluster user interface (RunAI). Requires on-campus network access or VPN.' + description: 'Apollo GPU cluster user interface (RunAI). Requires on-campus network access or VPN.', }, 'gpu/guide': { url: 'https://sydney-informatics-hub.github.io/gpu-cluster-onboarding-guide/', - description: 'Apollo GPU cluster onboarding guide and tutorials' + description: 'Apollo GPU cluster onboarding guide and tutorials', }, 'hacky-hour-zoom': { url: 'https://uni-sydney.zoom.us/my/sih.hackyhour', - description: 'Zoom link for our monthly SIH Hacky Hour, on the 3rd Wednesday of every month at 2pm.' - }, + description: 'Zoom link for our monthly SIH Hacky Hour, on the 3rd Wednesday of every month at 2pm.', + }, 'hf': { url: 'https://huggingface.co/SIH', - description: 'SIH Hugging Face organization page' + description: 'SIH Hugging Face organization page', }, 'hpc/docs': { url: 'https://sydneyuni.atlassian.net/wiki/spaces/RC/pages/3448733944/Sydney+HPC+Scheme', - description: 'Sydney HPC Scheme Confluence page' + description: 'Sydney HPC Scheme Confluence page', }, 'hpc/costs': { url: 'https://intranet.sydney.edu.au/research-support/managing-research/research-computing.html#cost', - description: 'University of Sydney High Performance Computing Cost Tables' + description: 'University of Sydney High Performance Computing Cost Tables', }, 'hpc/chat': { url: 'https://app.cogniti.ai/agents/68099883ab29fb8c2af4154d/chat?k=e8f0C6vSYYuEh40obfkH3IRxlwGDrM6T1bCYXyknfm4', - description: 'University of Sydney High Performance Computing AI Chat Assistant' + description: 'University of Sydney High Performance Computing AI Chat Assistant', }, 'hpc/dropin': { url: 'https://uni-sydney.zoom.us/j/85100208525?from=addon', - description: 'Zoom link for our monthly HPC drop-in sessions, on the 1st Thursday of every month at 12pm.' + description: 'Zoom link for our monthly HPC drop-in sessions, on the 1st Thursday of every month at 12pm.', }, 'hpc/nci': { url: 'https://nci.sydney.edu.au/', - description: 'Sydney national HPC scheme administration portal' + description: 'Sydney national HPC scheme administration portal', }, 'hpc/nci-variable-scheme': { url: 'https://sydneyuni.atlassian.net/wiki/spaces/RC/pages/3722018879/Power+Users#Purchased-KSU', - description: 'NCI variable scheme instructions for purchasing additional KSU' + description: 'NCI variable scheme instructions for purchasing additional KSU', }, 'hpc/policies': { url: 'https://sydneyuni.atlassian.net/wiki/spaces/RC/pages/4605805979/Policies', - description: 'Sydney HPC Scheme policies' + description: 'Sydney HPC Scheme policies', }, 'ipa-guide': { url: 'https://github.com/Sydney-Informatics-Hub/IPA-user-guide/blob/main/20260616%20IPA%20user%20guide.pdf', - description: 'Ingenuity Pathway Analysis User Guide' - }, + description: 'Ingenuity Pathway Analysis User Guide', + }, 'jira': { url: 'https://ctdshub.atlassian.net/jira/software/c/projects/PIPE/boards/27', - description: 'SIH JIRA Project management and issue tracking' + description: 'SIH JIRA Project management and issue tracking', }, 'jira-data-entry': { url: 'https://sydney.au1.qualtrics.com/jfe/form/SV_9LxgSqP0TjNwOXA', @@ -158,138 +158,137 @@ const REDIRECTS = { }, 'label': { url: 'http://10.122.246.109:8080/', - description: 'Labelstudio deployment (needs VPN to access)' + description: 'Labelstudio deployment (needs VPN to access)', }, 'ldaca': { url: 'https://milysun.github.io/wordflow-workshop/', - description: 'Wordflow Workshops Landing Page' + description: 'Wordflow Workshops Landing Page', }, 'leave': { url: 'https://unisyd.sharepoint.com/:x:/r/teams/SydneyInformaticsHub2/Shared%20Documents/1%20SIH%20Central%20Document%20Repository/Admin/Leave%20and%20Travel%20Schedule/SIH_Annual_Leave_and_Business_Travel_Schedule_2026.xlsx?d=wdb8cbe2e7e614bb9889bc5a8136722e1&csf=1&web=1&e=aSUUBA', - description: 'SIH Leave Calendar 2026' + description: 'SIH Leave Calendar 2026', }, 'mailing-list': { url: 'https://signup.e2ma.net/signup/1945889/1928048/', - description: 'Sydney Informatics Hub News + Training Mailing lists signup page' + description: 'Sydney Informatics Hub News + Training Mailing lists signup page', }, 'materials': { url: 'https://unisyd.sharepoint.com/teams/SydneyInformaticsHub2/Share', - description: 'SIH shared training materials and other university-wide shared documents' + description: 'SIH shared training materials and other university-wide shared documents', }, 'monday-update': { url: 'https://ctdshub.atlassian.net/wiki/spaces/INFRA/pages/602537987/Monday+Meeting+Project+Update+Presentation+Roster', - description: 'SIH Monday fortnightly meeting 5-min Project Update Calendar' + description: 'SIH Monday fortnightly meeting 5-min Project Update Calendar', }, 'navigator': { url: 'https://app.cogniti.ai/agents/6539de2436d0b628561cad9c/chat?k=tz0LR06KDQMgOaq507-pvaSrCzGwfCHjo829dJLTM4s', - description: 'University of Sydney Core Research Facility Navigator Chatbot' + description: 'University of Sydney Core Research Facility Navigator Chatbot', }, 'hpc/nci-access': { url: 'https://sydneyuni.atlassian.net/wiki/spaces/RC/pages/3722182703/NCI+Gadi+HPC', - description: 'NCI access via Sydney HPC Scheme' + description: 'NCI access via Sydney HPC Scheme', }, 'nci-su-calculator': { url: 'https://sydney-informatics-hub.github.io/nci-su-calculator/', - description: 'Service unit calculator for NCI Gadi HPC' + description: 'Service unit calculator for NCI Gadi HPC', }, 'pawsey-onboarding': { url: 'https://sydney.au1.qualtrics.com/jfe/form/SV_5mXyhFZsPIwZDBs?SupportType=Pawsey', - description: 'Pawsey Setonix HPC onboarding form' + description: 'Pawsey Setonix HPC onboarding form', }, 'hpc/pawsey-usyd-guide': { url: 'https://sydney-informatics-hub.github.io/usyd-pawsey-onboarding-guide/', - description: 'Pawsey Setonix HPC guide for USyd' + description: 'Pawsey Setonix HPC guide for USyd', }, 'hpc/pawsey-usyd-docs': { url: 'https://sydneyuni.atlassian.net/wiki/spaces/RC/pages/3774775342/Pawsey+Setonix+HPC', - description: 'Pawsey Setonix HPC documentation for USyd' + description: 'Pawsey Setonix HPC documentation for USyd', }, 'ref-check': { url: 'https://github.com/Sydney-Informatics-Hub/RefCheckAI', - description: 'SIH AI Reference Checker: Automated citation validation for AI-generated and academic content.' - }, + description: 'SIH AI Reference Checker: Automated citation validation for AI-generated and academic content.', + }, 'request': { url: 'https://sydney.au1.qualtrics.com/jfe/form/SV_5mXyhFZsPIwZDBs', - description: 'Sydney Informatics Hub request for assistance form.' + description: 'Sydney Informatics Hub request for assistance form.', }, 'research-compute-guides': { url: 'https://sydneyuni.atlassian.net/wiki/spaces/RC/overview?homepageId=173932782', - description: 'Technical documentation for research computing infrastructure at Sydney' + description: 'Technical documentation for research computing infrastructure at Sydney', }, 'research-compute-tools': { url: 'https://github.com/Sydney-Informatics-Hub/research-computing-tools', - description: 'SIH research computing resources and tools' + description: 'SIH research computing resources and tools', }, 'research-pulse': { url: 'https://github.com/Sydney-Informatics-Hub/ResearchPulseAI', - description: 'ResearchPulseAI: AI-driven Research Impact Assessment Tool and Case Study Generator for NSW Health' + description: 'ResearchPulseAI: AI-driven Research Impact Assessment Tool and Case Study Generator for NSW Health', }, 'sharepoint': { url: 'https://unisyd.sharepoint.com/:f:/r/teams/SydneyInformaticsHub2/Shared Documents/1 SIH Central Document Repository', - description: 'SIH Central document repository' + description: 'SIH Central document repository', }, 'sih-sharepoint-site': { url: 'https://unisyd.sharepoint.com/teams/SydneyInformaticsHub2/SitePages/00_landing_page.aspx', - description: 'SIH Staff Home (previously confluence)' + description: 'SIH Staff Home (previously confluence)', }, 'skill': { url: 'https://github.sydney.edu.au/informatics/trainingmaterials/tree/master/CrossSkilling', - description: 'SIH Cross-skilling calendar + materials' + description: 'SIH Cross-skilling calendar + materials', }, - 'src':{ + 'src': { url: 'https://intranet.sydney.edu.au/research-support/managing-research/facilities-and-tools/sydney-research-cloud.html', - description: 'Sydney Research Cloud information on the Intranet' + description: 'Sydney Research Cloud information on the Intranet', }, 'stats': { url: 'https://sydney-informatics-hub.github.io/stats-resources/', - description: 'SIH Statistics and data analysis resources' + description: 'SIH Statistics and data analysis resources', }, 'structural-biology-platform': { url: 'https://www.biocommons.org.au/structural-biology-platform', - description: 'Structural Biology workflow service developed by Sydney Informatics Hub in partnership with the Australian Biocommons and UNSW' + description: 'Structural Biology workflow service developed by Sydney Informatics Hub in partnership with the Australian Biocommons and UNSW', }, 'team-guides': { url: 'https://unisyd.sharepoint.com/:f:/r/teams/SydneyInformaticsHub2/Shared Documents/1 SIH Central Document Repository/Team Folders/Informatics/Team Guides', - description: 'SIH Informatics Team Guides' + description: 'SIH Informatics Team Guides', }, 'things': { url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', - description: 'Important things' + description: 'Important things', }, 'training': { url: 'https://www.sydney.edu.au/research/facilities/sydney-informatics-hub/workshops-and-training/training-calendar.html', - description: 'Training calendar' + description: 'Training calendar', }, 'training-dashboard': { url: 'https://app.powerbi.com/links/ZTQdGqm_Uk', - description: 'SIH Training Dashboard' + description: 'SIH Training Dashboard', }, 'upcoming': { url: 'https://unisyd.sharepoint.com/:l:/r/teams/SydneyInformaticsHub2/Lists/Upcoming Informatics Team Events?e=cdJqwB', - description: 'Upcoming Informatics Team Events' + description: 'Upcoming Informatics Team Events', }, 'vm-register': { url: 'https://unisyd.sharepoint.com/:x:/r/teams/SydneyInformaticsHub2/Shared Documents/1 SIH Central Document Repository/Team Folders/Informatics/VM and asset management/VM register.xlsx?web=1', - description: 'SIH team VM register' + description: 'SIH team VM register', }, 'wimr-consult': { url: 'https://outlook.office.com/book/StatisticalConsultwithAlexandraGreenandAmarinderThind@unisyd.onmicrosoft.com/?ismsaljsauthenabled', - description: 'WIMR biostatistics consult booking form' + description: 'WIMR biostatistics consult booking form', }, 'wordflow': { url: 'https://australian-text-analytics-platform.github.io/LDaCa_Text_Analytics_Tools/', - description: 'LDaCA Wordflow Landing Page' + description: 'LDaCA Wordflow Landing Page', }, 'youtube': { url: 'https://www.youtube.com/@sydneyinformaticshub', - description: 'Sydney Informatics Hub - YouTube' + description: 'Sydney Informatics Hub - YouTube', }, }; -// Template: -//, +// Template: copy the lines below (including the final comma), paste them +// above the `};` line and remove the leading `//`. Order doesn't matter. // 'shortlink': { -// url:'', -// description:'' -// } -// +// url: 'https://example.com', +// description: 'What this link is', +// }, diff --git a/lychee.toml b/lychee.toml new file mode 100644 index 0000000..adb92a6 --- /dev/null +++ b/lychee.toml @@ -0,0 +1,16 @@ +# Broken-link checker config (https://lychee.cli.rs) +# 401/403/429 usually mean "needs login" or rate-limiting, not a dead link. +accept = ["100..=103", "200..=299", "401", "403", "429"] +max_retries = 2 +timeout = 20 +user_agent = "Mozilla/5.0 (compatible; sih-links-checker)" + +# Internal / login-only hosts that can't be reached from GitHub's runners. +exclude_all_private = true +exclude = [ + 'sydney\.edu\.au', + 'sharepoint\.com', + 'office\.com', + 'powerbi\.com', + 'atlassian\.net', +]