From 3fa65699d52246eb6fd23c049070da7de294dc2e Mon Sep 17 00:00:00 2001 From: Vatsal Sanjay Date: Tue, 28 Jul 2026 22:01:42 +0100 Subject: [PATCH 1/3] Harden site build and search boundaries Pin browser and build dependencies, constrain untrusted search URLs, and fail closed on local tooling errors. Restrict Actions tokens, move Pages credentials to deploy jobs, upgrade Ruby, and fix the affected UI and packaging regressions. --- .github/workflows/jekyll.yml | 9 ++--- .../workflows/rebuild-on-search-update.yml | 9 ++--- .github/workflows/teaching-content-checks.yml | 9 +++-- .jekyllignore | 1 + .ruby-version | 2 +- CNAME | 2 +- Gemfile | 2 +- Gemfile.lock | 2 +- README.md | 4 +-- _config.yml | 1 + _layouts/default.html | 4 ++- _layouts/history.html | 4 ++- _layouts/join-us.html | 4 ++- _layouts/team.html | 4 ++- about-legacy.html | 15 ++++++++ assets/css/join-us.css | 10 +++--- assets/css/research.css | 4 +-- assets/css/styles.css | 15 +++++--- assets/js/main.js | 2 +- assets/js/search-manager.js | 30 ++++++++++++++-- eslint.config.js | 15 +++++--- package-lock.json | 2 ++ package.json | 4 ++- scripts/fix-line-length.js | 3 +- scripts/generate_seo_tags.rb | 5 ++- scripts/lint-check.sh | 12 +++++-- scripts/setup.sh | 2 +- tests/fix-line-length-actual.test.js | 16 ++++++++- tests/search-manager.test.js | 36 +++++++++++++++++++ 29 files changed, 177 insertions(+), 51 deletions(-) create mode 100644 about-legacy.html diff --git a/.github/workflows/jekyll.yml b/.github/workflows/jekyll.yml index 6d666593..10969311 100644 --- a/.github/workflows/jekyll.yml +++ b/.github/workflows/jekyll.yml @@ -8,8 +8,6 @@ on: permissions: contents: read - pages: write - id-token: write concurrency: group: jekyll-site-${{ github.ref }} @@ -24,7 +22,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: "3.2.2" + ruby-version: "3.4.10" bundler: "2.5.23" bundler-cache: true @@ -38,7 +36,7 @@ jobs: - name: Package Pages artifact run: | - tar --dereference --hard-dereference -cvf github-pages.tar _site + tar --dereference --hard-dereference --directory _site -cvf ../github-pages.tar . gzip -f github-pages.tar - name: Upload artifact @@ -54,6 +52,9 @@ jobs: if: github.event_name == 'push' && github.ref == 'refs/heads/main' needs: build runs-on: ubuntu-latest + permissions: + pages: write + id-token: write env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true environment: diff --git a/.github/workflows/rebuild-on-search-update.yml b/.github/workflows/rebuild-on-search-update.yml index 8c41fd54..e4b69d6a 100644 --- a/.github/workflows/rebuild-on-search-update.yml +++ b/.github/workflows/rebuild-on-search-update.yml @@ -8,8 +8,6 @@ on: permissions: contents: read - pages: write - id-token: write jobs: build: @@ -20,7 +18,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: "3.2.2" + ruby-version: "3.4.10" bundler: "2.5.23" bundler-cache: true @@ -31,7 +29,7 @@ jobs: - name: Package Pages artifact run: | - tar --dereference --hard-dereference -cvf github-pages.tar _site + tar --dereference --hard-dereference --directory _site -cvf ../github-pages.tar . gzip -f github-pages.tar - name: Upload artifact @@ -45,6 +43,9 @@ jobs: deploy: needs: build runs-on: ubuntu-latest + permissions: + pages: write + id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} diff --git a/.github/workflows/teaching-content-checks.yml b/.github/workflows/teaching-content-checks.yml index 7988d3e5..4499de09 100644 --- a/.github/workflows/teaching-content-checks.yml +++ b/.github/workflows/teaching-content-checks.yml @@ -20,6 +20,9 @@ on: - "package.json" - "package-lock.json" +permissions: + contents: read + jobs: markdown-quality: runs-on: ubuntu-latest @@ -35,16 +38,16 @@ jobs: cache: "npm" - name: Install dependencies - run: npm ci + run: npm ci --ignore-scripts - name: Run markdownlint for teaching content run: | - npx markdownlint-cli2 --config .markdownlint-cli2.jsonc \ + ./node_modules/.bin/markdownlint-cli2 --config .markdownlint-cli2.jsonc \ "_teaching/**/*.md" \ "assets/images/teaching/README.md" - name: Check prettier formatting for teaching content run: | - npx prettier --check \ + ./node_modules/.bin/prettier --check \ "_teaching/**/*.md" \ "assets/images/teaching/README.md" diff --git a/.jekyllignore b/.jekyllignore index 3d477435..46476492 100644 --- a/.jekyllignore +++ b/.jekyllignore @@ -1,4 +1,5 @@ .cursor/ .claude/ CLAUDE.md +AGENTS.md .gitignore diff --git a/.ruby-version b/.ruby-version index be94e6f5..84d6c676 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.2.2 +3.4.10 diff --git a/CNAME b/CNAME index e143a36f..82f1fd05 100644 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ -https://comphy-lab.org \ No newline at end of file +comphy-lab.org diff --git a/Gemfile b/Gemfile index 6fcc32f3..505f7d7e 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source "https://rubygems.org" -ruby "~> 3.2.0" +ruby "~> 3.4.0" gem "jekyll", "~> 4.3.2" gem "webrick", "~> 1.8" diff --git a/Gemfile.lock b/Gemfile.lock index 6f8eb9fa..d270f29e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -184,7 +184,7 @@ DEPENDENCIES webrick (~> 1.8) RUBY VERSION - ruby 3.2.2p53 + ruby 3.4.10 BUNDLED WITH 2.5.23 diff --git a/README.md b/README.md index 656644f1..1e29b3ba 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ A static website for the Computational Multiphase Physics Laboratory, built with 2. **Manual Setup (Alternative)** Prerequisites: - - Ruby `3.2.2` (matches `.ruby-version`) + - Ruby `3.4.10` (matches `.ruby-version`) - Bundler `2.5.23` (`gem install bundler -v 2.5.23`) - Node.js and npm (for linting and testing) @@ -839,7 +839,7 @@ The repository uses automated tools to ensure code quality and consistency: ```bash npm install - npx husky install + ./node_modules/.bin/husky install ``` #### Pre-commit Hooks diff --git a/_config.yml b/_config.yml index 24a1cea9..39d4e34c 100644 --- a/_config.yml +++ b/_config.yml @@ -45,6 +45,7 @@ exclude: - .claude - .claude/** - CLAUDE.md + - AGENTS.md # Design-system handoff bundle — reference only, not built - comphy-lab-design-system - comphy-lab-design-system/** diff --git a/_layouts/default.html b/_layouts/default.html index 1df94023..4def6941 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -130,7 +130,9 @@ - + diff --git a/_layouts/history.html b/_layouts/history.html index ef65c281..d013e375 100644 --- a/_layouts/history.html +++ b/_layouts/history.html @@ -82,7 +82,9 @@ - + diff --git a/_layouts/join-us.html b/_layouts/join-us.html index 38546625..929bcdf5 100644 --- a/_layouts/join-us.html +++ b/_layouts/join-us.html @@ -59,7 +59,9 @@ - + diff --git a/_layouts/team.html b/_layouts/team.html index a1264424..ea54bd9d 100644 --- a/_layouts/team.html +++ b/_layouts/team.html @@ -76,7 +76,9 @@ - + diff --git a/about-legacy.html b/about-legacy.html new file mode 100644 index 00000000..a41135b5 --- /dev/null +++ b/about-legacy.html @@ -0,0 +1,15 @@ +--- +layout: default +title: Redirecting to CoMPhy Lab +permalink: /about.html +sitemap: false +--- + + + +
+

About has moved to the homepage.

+

If you are not redirected automatically, click here.

+
diff --git a/assets/css/join-us.css b/assets/css/join-us.css index 73e2f660..134d2d54 100644 --- a/assets/css/join-us.css +++ b/assets/css/join-us.css @@ -202,7 +202,7 @@ margin: 4rem 0; } -.project-category h3 { +.project-category > h3 { color: var(--join-subheading-color); font-size: 2.2rem; margin-bottom: 2rem; @@ -211,16 +211,16 @@ padding-bottom: 0; } -.project-category h3:after { +.project-category > h3:after { content: none; } /* Different accent colors for each category */ -.soft-matter-category h3:after { +.soft-matter-category > h3:after { background-color: var(--join-phd-accent); } -.free-surface-category h3:after { +.free-surface-category > h3:after { background-color: var(--join-postdoc-accent); } @@ -321,7 +321,7 @@ font-size: 2.4rem; } - .project-category h3 { + .project-category > h3 { font-size: 2rem; } } diff --git a/assets/css/research.css b/assets/css/research.css index c779c347..2ba98fec 100644 --- a/assets/css/research.css +++ b/assets/css/research.css @@ -700,7 +700,7 @@ } .research-content .tags span:nth-of-type(3) { - background: var(--color-primary-alpha-5); + background: var(--color-primary-alpha-05); } [data-theme="dark"] .research-content .tags span:nth-of-type(3) { @@ -708,7 +708,7 @@ } .research-content .tags span:nth-of-type(n + 4) { - background: var(--color-primary-alpha-5); + background: var(--color-primary-alpha-05); } [data-theme="dark"] .research-content .tags span:nth-of-type(n + 4) { diff --git a/assets/css/styles.css b/assets/css/styles.css index 899ddba3..f5e4f93c 100644 --- a/assets/css/styles.css +++ b/assets/css/styles.css @@ -76,6 +76,7 @@ /* Color System - Transparent Colors */ --color-black-alpha-05: rgba(0, 0, 0, 0.05); + --color-black-alpha-08: rgba(0, 0, 0, 0.08); --color-black-alpha-10: rgba(0, 0, 0, 0.1); --color-black-alpha-15: rgba(0, 0, 0, 0.15); --color-black-alpha-20: rgba(0, 0, 0, 0.2); @@ -89,6 +90,7 @@ --color-white-alpha-30: rgba(255, 255, 255, 0.3); --color-white-alpha-35: rgba(255, 255, 255, 0.35); --color-white-alpha-40: rgba(255, 255, 255, 0.4); + --color-white-alpha-50: rgba(255, 255, 255, 0.5); --color-white-alpha-70: rgba(255, 255, 255, 0.7); --color-white-alpha-85: rgba(255, 255, 255, 0.85); --color-white-alpha-95: rgba(255, 255, 255, 0.95); @@ -105,7 +107,9 @@ --color-blue-bright-alpha-20: rgba(76, 110, 245, 0.2); --color-blue-bright-alpha-25: rgba(76, 110, 245, 0.25); --color-blue-bright-alpha-30: rgba(76, 110, 245, 0.3); + --color-blue-bright-alpha-40: rgba(76, 110, 245, 0.4); --color-blue-bright-alpha-50: rgba(76, 110, 245, 0.5); + --color-blue-bright-alpha-80: rgba(76, 110, 245, 0.8); --color-red-darker-alpha-20: rgba(250, 82, 82, 0.2); --color-header-base-alpha-15: rgba(104, 35, 109, 0.15); --color-header-base-alpha-20: rgba(104, 35, 109, 0.2); @@ -129,6 +133,7 @@ /* Transitions */ --transition-fast: all 0.2s ease; --transition-medium: all 0.3s ease; + --loader-border-color: var(--color-black-alpha-10); /* Spacing System */ --space-xs: 0.5rem; @@ -2323,7 +2328,7 @@ section#featured:after, } /* Join page markdown refinements */ -.join-content .markdown-content h2 { +.join-content.markdown-content h2 { font-size: 2.4rem; color: var(--color-primary); margin: 2.4rem 0 1.2rem; @@ -2331,17 +2336,17 @@ section#featured:after, border-bottom: 2px solid var(--position-h2-border); } -.join-content .markdown-content p { +.join-content.markdown-content p { margin: 0 0 1.2rem; } -.join-content .markdown-content ul, -.join-content .markdown-content ol { +.join-content.markdown-content ul, +.join-content.markdown-content ol { margin: 0 0 1.2rem 2rem; padding: 0; } -.join-content .markdown-content li { +.join-content.markdown-content li { margin: 0.4rem 0; } diff --git a/assets/js/main.js b/assets/js/main.js index 841549c7..ee76d91d 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -445,7 +445,7 @@ // If not on home page, let the browser handle the navigation } else if (href.startsWith("#")) { e.preventDefault(); - const target = document.querySelector(href); + const target = document.getElementById(href.substring(1)); if (target) { target.scrollIntoView({ behavior: "smooth", diff --git a/assets/js/search-manager.js b/assets/js/search-manager.js index e86047dc..2617c181 100644 --- a/assets/js/search-manager.js +++ b/assets/js/search-manager.js @@ -32,6 +32,29 @@ /^(?:Private|0_todo|Admin|Medical|Personal)\b/i, ]; + const PUBLIC_SEARCH_HOSTS = new Set([ + "comphy-lab.org", + "blogs.comphy-lab.org", + ]); + + function isSafeSearchUrl(value) { + if (typeof value !== "string" || value.length === 0) { + return false; + } + + try { + const parsed = new URL(value, window.location.origin); + return ( + (parsed.protocol === "https:" || parsed.protocol === "http:") && + PUBLIC_SEARCH_HOSTS.has(parsed.hostname) && + parsed.username === "" && + parsed.password === "" + ); + } catch (_error) { + return false; + } + } + function isInternalSearchEntry(entry) { const entryIdentityText = [entry?.title, entry?.url] .filter(Boolean) @@ -43,7 +66,10 @@ } function sanitizeSearchData(data) { - return data.filter((entry) => !isInternalSearchEntry(entry)); + return data.filter( + (entry) => + !isInternalSearchEntry(entry) && isSafeSearchUrl(entry?.url) + ); } /** @@ -199,7 +225,7 @@ id: `search-result-${result.refIndex}`, title: result.item.title || "Untitled", handler: () => { - if (result.item.url) { + if (isSafeSearchUrl(result.item.url)) { window.location.href = result.item.url; } }, diff --git a/eslint.config.js b/eslint.config.js index 88b7d4f4..b2d277a4 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,13 +1,18 @@ +const js = require("@eslint/js"); +const globals = require("globals"); + module.exports = [ + js.configs.recommended, { languageOptions: { - ecmaVersion: 12, - sourceType: "module", + ecmaVersion: 2021, + sourceType: "script", globals: { - browser: true, - es2021: true, + ...globals.browser, + ...globals.es2021, marked: "readonly", - DOMPurify: "readonly" + DOMPurify: "readonly", + Utils: "readonly" } }, rules: { diff --git a/package-lock.json b/package-lock.json index acf0ef78..a54b8011 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,9 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { + "@eslint/js": "8.57.1", "eslint": "^8.56.0", + "globals": "13.24.0", "husky": "^8.0.3", "jest": "^30.3.0", "jest-environment-jsdom": "^30.3.0", diff --git a/package.json b/package.json index 58716b66..0bd63bce 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "lint:md": "markdownlint-cli2 --config .markdownlint-cli2.jsonc", "lint": "npm run lint:js && npm run lint:css && npm run lint:md", "format": "prettier --write assets/js/**/*.js assets/css/**/*.css", - "prepare": "npx husky install", + "prepare": "husky install", "test": "jest --coverage" }, "repository": { @@ -29,7 +29,9 @@ }, "homepage": "https://comphy-lab.org", "devDependencies": { + "@eslint/js": "8.57.1", "eslint": "^8.56.0", + "globals": "13.24.0", "husky": "^8.0.3", "jest": "^30.3.0", "jest-environment-jsdom": "^30.3.0", diff --git a/scripts/fix-line-length.js b/scripts/fix-line-length.js index 9ca0c86b..73ee6c34 100644 --- a/scripts/fix-line-length.js +++ b/scripts/fix-line-length.js @@ -217,6 +217,7 @@ function processJsFiles() { fixedFiles++; } catch (error) { console.error(`Error writing to file ${file}:`, error.message); + throw error; } } } @@ -228,4 +229,4 @@ function processJsFiles() { } // Run the script -processJsFiles(); \ No newline at end of file +processJsFiles(); diff --git a/scripts/generate_seo_tags.rb b/scripts/generate_seo_tags.rb index 1f0be6ec..59dee1d2 100755 --- a/scripts/generate_seo_tags.rb +++ b/scripts/generate_seo_tags.rb @@ -87,10 +87,9 @@ def normalize_url(url) end normalized = Pathname.new(url).cleanpath.to_s - return nil unless normalized.start_with?('/') - # Remove leading slash for file operations result = normalized.sub(/^\//, '') + return nil if result.start_with?('~') # Debug output puts "URL Normalization: #{original_url} -> #{result}" if ENV['DEBUG'] @@ -285,8 +284,8 @@ def update_description_meta(doc, head, description) descriptions = descriptions_by_url[url] || Set.new # Get file path - file_path = File.expand_path(url, site_dir) site_root = File.expand_path(site_dir) + file_path = File.expand_path(File.join(site_root, url)) unless file_path.start_with?("#{site_root}#{File::SEPARATOR}") puts "Skipping unsafe output path for #{url}" if ENV['DEBUG'] diff --git a/scripts/lint-check.sh b/scripts/lint-check.sh index b0e428e2..54eca9fc 100755 --- a/scripts/lint-check.sh +++ b/scripts/lint-check.sh @@ -110,9 +110,15 @@ fi # Run ESLint on JavaScript files with quotes rule JS_DIR="$REPO_ROOT/assets/js" +ESLINT_BIN="$REPO_ROOT/node_modules/.bin/eslint" + +if [[ ! -x "$ESLINT_BIN" ]]; then + echo "Local ESLint is unavailable. Run npm ci first." >&2 + exit 1 +fi if [[ "$FIX_MODE" == "true" ]]; then # Fix mode: run ESLint with --fix flag - npx eslint "$JS_DIR" --rule 'quotes: ["error", "double"]' --fix + "$ESLINT_BIN" "$JS_DIR" --rule 'quotes: ["error", "double"]' --fix if [ $? -eq 0 ]; then echo "Fixed quote style in JavaScript files." else @@ -120,7 +126,7 @@ if [[ "$FIX_MODE" == "true" ]]; then fi else # Check-only mode: run ESLint without --fix - npx eslint "$JS_DIR" --rule 'quotes: ["error", "double"]' + "$ESLINT_BIN" "$JS_DIR" --rule 'quotes: ["error", "double"]' ESLINT_EXIT_CODE=$? if [ $ESLINT_EXIT_CODE -eq 0 ]; then echo "No quote style issues found in JavaScript files." @@ -130,4 +136,4 @@ else fi fi -echo "Lint check completed!" \ No newline at end of file +echo "Lint check completed!" diff --git a/scripts/setup.sh b/scripts/setup.sh index 95c5603b..fc26b9f8 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -151,7 +151,7 @@ echo "🔨 Building site and generating search database..." # Install husky hooks echo "" echo "🪝 Installing Git hooks (husky)..." -npx husky install +./node_modules/.bin/husky install # Run simple validation tests echo "" diff --git a/tests/fix-line-length-actual.test.js b/tests/fix-line-length-actual.test.js index 50a2ef91..e7a75438 100644 --- a/tests/fix-line-length-actual.test.js +++ b/tests/fix-line-length-actual.test.js @@ -118,6 +118,20 @@ describe('fix-line-length.js actual implementation', () => { expect(mockFs.writeFileSync).toHaveBeenCalledTimes(0); }); + it('should fail when a rewritten file cannot be written', () => { + mockFs.readdirSync.mockReturnValue(['long-lines.js']); + mockFs.readFileSync.mockReturnValue( + 'const value = {first: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", second: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"};' + ); + mockFs.writeFileSync.mockImplementation(() => { + throw new Error('Permission denied'); + }); + + expect(() => { + require('../scripts/fix-line-length.js'); + }).toThrow('Permission denied'); + }); + it('should log summary of processed files', () => { mockFs.readdirSync.mockReturnValue(['test1.js', 'test2.js']); mockFs.readFileSync.mockReturnValue('short line'); @@ -129,4 +143,4 @@ describe('fix-line-length.js actual implementation', () => { expect(console.log).toHaveBeenCalledWith(expect.stringContaining('0 lines exceeded 80 characters')); expect(console.log).toHaveBeenCalledWith(expect.stringContaining('0 files were modified')); }); -}); \ No newline at end of file +}); diff --git a/tests/search-manager.test.js b/tests/search-manager.test.js index 229245b1..1c303bb2 100644 --- a/tests/search-manager.test.js +++ b/tests/search-manager.test.js @@ -79,4 +79,40 @@ describe("search-manager sanitization", () => { "https://comphy-lab.org/.agents/skills/add-paper/SKILL/#badge-shapes" ); }); + + it("rejects unsafe and unapproved search result URLs", async () => { + global.fetch = jest.fn().mockResolvedValue({ + ok: true, + json: () => + Promise.resolve([ + { + title: "Script URL", + url: "javascript:alert(document.domain)", + content: "malicious search entry", + }, + { + title: "Credentialed URL", + url: "https://attacker@comphy-lab.org/", + content: "misleading search entry", + }, + { + title: "External redirect", + url: "https://example.net/phishing", + content: "unapproved search entry", + }, + { + title: "Public blog post", + url: "https://blogs.comphy-lab.org/public-post/", + content: "approved search entry", + }, + ]), + }); + + require("../assets/js/search-manager.js"); + + const data = await window.SearchManager.loadSearchDatabase(); + + expect(data).toHaveLength(1); + expect(data[0].title).toBe("Public blog post"); + }); }); From 5b96eb854a07fe63a2ab5608d909e513f7f3ae72 Mon Sep 17 00:00:00 2001 From: Vatsal Sanjay Date: Wed, 29 Jul 2026 08:38:37 +0100 Subject: [PATCH 2/3] Declare Ruby 3.4 standard library gems Add base64 and csv as explicit dependencies now that Ruby 3.4 no longer ships them as default gems. This keeps the pinned Jekyll build runnable locally and in CI. --- Gemfile | 2 ++ Gemfile.lock | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/Gemfile b/Gemfile index 505f7d7e..25a37536 100644 --- a/Gemfile +++ b/Gemfile @@ -3,6 +3,8 @@ source "https://rubygems.org" ruby "~> 3.4.0" gem "jekyll", "~> 4.3.2" +gem "base64" +gem "csv" gem "webrick", "~> 1.8" gem "jekyll-sitemap" gem "jekyll-include-cache" diff --git a/Gemfile.lock b/Gemfile.lock index d270f29e..c74066ad 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,9 +3,11 @@ GEM specs: addressable (2.9.0) public_suffix (>= 2.0.2, < 8.0) + base64 (0.3.0) bigdecimal (3.1.9) colorator (1.1.0) concurrent-ruby (1.3.7) + csv (3.3.6) em-websocket (0.5.3) eventmachine (>= 0.12.9) http_parser.rb (~> 0) @@ -176,6 +178,8 @@ PLATFORMS x86_64-linux-musl DEPENDENCIES + base64 + csv jekyll (~> 4.3.2) jekyll-include-cache jekyll-seo-tag From 89a60a986bc046aed6a8a0aa3df91eb593604892 Mon Sep 17 00:00:00 2001 From: Vatsal Sanjay Date: Wed, 29 Jul 2026 08:54:26 +0100 Subject: [PATCH 3/3] Fix Pages artifact packaging path Write the Pages archive to the explicit GitHub workspace path so tar and gzip agree across runners. Drop the unused GNU-only hard-link flag to keep local reproduction portable. --- .github/workflows/jekyll.yml | 6 ++++-- .github/workflows/rebuild-on-search-update.yml | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/jekyll.yml b/.github/workflows/jekyll.yml index 10969311..5c848084 100644 --- a/.github/workflows/jekyll.yml +++ b/.github/workflows/jekyll.yml @@ -36,8 +36,10 @@ jobs: - name: Package Pages artifact run: | - tar --dereference --hard-dereference --directory _site -cvf ../github-pages.tar . - gzip -f github-pages.tar + tar --dereference \ + --directory "${GITHUB_WORKSPACE}/_site" \ + --create --file "${GITHUB_WORKSPACE}/github-pages.tar" . + gzip -f "${GITHUB_WORKSPACE}/github-pages.tar" - name: Upload artifact uses: actions/upload-artifact@v6 diff --git a/.github/workflows/rebuild-on-search-update.yml b/.github/workflows/rebuild-on-search-update.yml index e4b69d6a..42c35c2c 100644 --- a/.github/workflows/rebuild-on-search-update.yml +++ b/.github/workflows/rebuild-on-search-update.yml @@ -29,8 +29,10 @@ jobs: - name: Package Pages artifact run: | - tar --dereference --hard-dereference --directory _site -cvf ../github-pages.tar . - gzip -f github-pages.tar + tar --dereference \ + --directory "${GITHUB_WORKSPACE}/_site" \ + --create --file "${GITHUB_WORKSPACE}/github-pages.tar" . + gzip -f "${GITHUB_WORKSPACE}/github-pages.tar" - name: Upload artifact uses: actions/upload-artifact@v6