diff --git a/.github/workflows/update-version-support.yml b/.github/workflows/update-version-support.yml new file mode 100644 index 0000000000..3d933d1a9f --- /dev/null +++ b/.github/workflows/update-version-support.yml @@ -0,0 +1,104 @@ +name: Update version support data + +on: + workflow_dispatch: + schedule: + # Nightly, an hour before the nightly docs update + - cron: "0 1 * * *" + +run-name: Nightly version support update + +env: + VERSION_INFO_URL: https://static.metabase.com/version-info.json + DATA_FILE: _data/major_version_support.json + +concurrency: + group: update-version-support + cancel-in-progress: false + +jobs: + update: + name: Update major_version_support.json + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + GH_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }} + BRANCH_NAME: update-version-support-${{ github.run_id }} + steps: + - uses: actions/checkout@v6 + with: + ref: master + token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }} + + - name: Fetch major_version_support from version-info.json + run: | + curl --fail --silent --show-error --retry 3 --retry-delay 5 \ + "$VERSION_INFO_URL" -o version-info.json + + # Bail out rather than commit garbage if the key is missing or empty + if ! jq -e '.major_version_support | arrays and length > 0' version-info.json > /dev/null; then + echo "::error::major_version_support is missing or empty in $VERSION_INFO_URL" + exit 1 + fi + + # Every entry needs a numeric major, otherwise the docs templates can't match on it + if ! jq -e '.major_version_support | all(.major | numbers)' version-info.json > /dev/null; then + echo "::error::every major_version_support entry must have a numeric \"major\"" + exit 1 + fi + + jq '.major_version_support' version-info.json > "$DATA_FILE" + rm version-info.json + + - name: Check for changes + id: diff + run: | + if git diff --quiet -- "$DATA_FILE"; then + echo "changed=false" >> "$GITHUB_OUTPUT" + echo "No changes to $DATA_FILE" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + git --no-pager diff -- "$DATA_FILE" + fi + + - name: Push the update + if: steps.diff.outputs.changed == 'true' + run: | + git config user.name github-actions + git config user.email github-actions@github.com + + git checkout -B "$BRANCH_NAME" + git add "$DATA_FILE" + git commit -m "Update major version support data" + git push --force origin "$BRANCH_NAME" + + - name: Open PR + id: create_pr + if: steps.diff.outputs.changed == 'true' + env: + GH_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }} + run: | # sh + DATE=$(date +'%Y-%m-%d') + + URL=$(gh pr create \ + --base master \ + --head "$BRANCH_NAME" \ + --title "Update version support data $DATE" \ + --body "Automated nightly update of major version support data.") + + PR_NUMBER=${URL##*/} + echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT" + + - name: Auto approve PR + if: steps.diff.outputs.changed == 'true' + uses: juliangruber/approve-pull-request-action@68fcc9a5a73b5641cadf757cf99d73720dcb05d0 # v2.1.0 + with: + github-token: ${{ github.token }} + number: ${{ steps.create_pr.outputs.pr_number }} + + - name: Enable Pull Request Automerge + if: steps.diff.outputs.changed == 'true' + run: gh pr merge --squash --auto "$PR_NUMBER" + env: + GH_TOKEN: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }} + PR_NUMBER: ${{ steps.create_pr.outputs.pr_number }} diff --git a/_config.yml b/_config.yml index a17efb596b..edda7245fe 100644 --- a/_config.yml +++ b/_config.yml @@ -18,6 +18,7 @@ whitelist: - jekyll_include_plugin - jekyll_dirname_payload_plugin - jekyll_responsive_table_labels_plugin + - jekyll_version_support_plugin jekyll_include_plugin: snippet_prefix: "" sass: diff --git a/_data/major_version_support.json b/_data/major_version_support.json new file mode 100644 index 0000000000..cff21a9a9c --- /dev/null +++ b/_data/major_version_support.json @@ -0,0 +1,62 @@ +[ + { + "major": 63, + "released": "2026-07-07", + "lts": false, + "eol": "2026-09-07" + }, + { + "major": 62, + "released": "2026-06-01", + "lts": false, + "eol": "2026-09-01" + }, + { + "major": 61, + "released": "2026-04-30", + "lts": false, + "eol": "2026-09-01" + }, + { + "major": 60, + "released": "2026-03-26", + "lts": false, + "eol": "2026-09-01" + }, + { + "major": 59, + "released": "2026-02-12", + "lts": false, + "eol": "2026-09-01" + }, + { + "major": 58, + "released": "2025-12-17", + "lts": true, + "eol": "2027-02-17" + }, + { + "major": 57, + "released": "2025-11-01", + "lts": false, + "eol": "2026-05-31" + }, + { + "major": 56, + "released": "2025-08-01", + "lts": false, + "eol": "2026-05-31" + }, + { + "major": 55, + "released": "2025-06-02", + "lts": false, + "eol": "2026-05-31" + }, + { + "major": 54, + "released": "2025-03-25", + "lts": false, + "eol": "2026-05-31" + } +] diff --git a/_includes/docs/blockquotes.html b/_includes/docs/blockquotes.html index 930ac45774..01c176d9bd 100644 --- a/_includes/docs/blockquotes.html +++ b/_includes/docs/blockquotes.html @@ -6,10 +6,17 @@ Check out the docs for the current stable version, Metabase {{site.docs_version}}. {% else %} -
- These are the docs for Metabase {{ page.version }}. - Check out the docs for the current stable version, Metabase {{site.docs_version}}. -+ {% assign support = site.data.version_support[page.version] %} + {% if support.status == "unsupported" %} +
+ Version {{ page.version }} of Metabase is no longer supported. Check out the docs for the current stable version, Metabase {{site.docs_version}}. ++ {% else %} +
+ These are the docs for Metabase {{ page.version }}. + Check out the docs for the current stable version, Metabase {{site.docs_version}}. ++ {% endif %} {% endif %} {% endif %} diff --git a/_includes/docs_version.html b/_includes/docs_version.html index 39337249a4..90034590c0 100644 --- a/_includes/docs_version.html +++ b/_includes/docs_version.html @@ -13,9 +13,19 @@
+ Version {{ page.version }} of Metabase is no longer supported. + Check out the docs for the current stable version, Metabase {{site.docs_version}}. ++ {% endif %} + {{ content }} diff --git a/_plugins/jekyll_version_support_plugin.rb b/_plugins/jekyll_version_support_plugin.rb new file mode 100644 index 0000000000..8b1b4c8345 --- /dev/null +++ b/_plugins/jekyll_version_support_plugin.rb @@ -0,0 +1,77 @@ +# Builds `site.data.version_support`, a lookup table of support status keyed by +# the version strings used throughout the site ("v0.63", "v0.57", ...). +# +# The source data is _data/major_version_support.json, refreshed nightly from +# the `major_version_support` key of https://static.metabase.com/version-info.json +# by .github/workflows/update-version-support.yml +# +# Templates just look a version up: +# +# {% assign support = site.data.version_support[page.version] %} +# {% if support.status == "unsupported" %}...{% endif %} +# {% if support.lts %}...{% endif %} +# +# The data file only tracks the most recent majors, so anything older than the +# oldest tracked major is unsupported. Versions with no entry and no verdict +# (such as "latest" and "master") are simply absent from the table, and a +# missing key renders as nil in Liquid — so callers need no special casing. +module Jekyll + class VersionSupportGenerator < Generator + safe true + priority :high + + SUPPORTED = "supported".freeze + UNSUPPORTED = "unsupported".freeze + + def generate(site) + entries = site.data["major_version_support"] || [] + by_major = entries.each_with_object({}) do |entry, acc| + major = entry["major"] + acc[major.to_i] = entry if major + end + + oldest_tracked = by_major.keys.min + today = site.time.to_date + + site.data["version_support"] = Array(site.config["available_versions"]) + .each_with_object({}) do |version, acc| + major = major_of(version) + status = status_for(major, by_major, oldest_tracked, today) + next if status.nil? + + acc[version] = { + "status" => status, + "lts" => by_major.dig(major, "lts") || false, + } + end + end + + private + + # "v0.63" -> 63 + def major_of(version) + version.to_s[/\Av\d+\.(\d+)\z/, 1]&.to_i + end + + def status_for(major, by_major, oldest_tracked, today) + return nil if major.nil? + + entry = by_major[major] + return UNSUPPORTED if entry.nil? && oldest_tracked && major < oldest_tracked + return nil if entry.nil? + + eol = parse_date(entry["eol"]) + return SUPPORTED if eol.nil? + + eol <= today ? UNSUPPORTED : SUPPORTED + end + + def parse_date(value) + return value.to_date if value.respond_to?(:to_date) && !value.is_a?(String) + + Date.parse(value.to_s) + rescue ArgumentError, TypeError + nil + end + end +end diff --git a/all.md b/all.md index 0a4b55df6c..ae718b8603 100644 --- a/all.md +++ b/all.md @@ -11,7 +11,10 @@ We version our docs for every major release, with docs for each major release co For product releases, see the list of [Metabase releases](https://github.com/metabase/metabase/releases). +Versions marked unsupported have passed their end-of-life date. See our [version support policy](/version-support) for details. + {% for version in site.available_versions reversed %} +{% assign support = site.data.version_support[version] %} -- [{{ version }}](/docs/{{ version }}/) +- [{{ version }}](/docs/{{ version }}/){% if support.status == "unsupported" %} unsupported{% elsif support.lts %} lts{% endif %} {% endfor %} diff --git a/docs/docs-local.css b/docs/docs-local.css new file mode 100644 index 0000000000..a8b2d7acca --- /dev/null +++ b/docs/docs-local.css @@ -0,0 +1,55 @@ +/* because stylesheets mainly live in the marketing repo, this is a subset of the styles + * that are specific to this repo, pending a larger refactor to move all docs-specific styles into this repo. */ + +.bootstrap .learn .version__dropdown { + width: auto !important; + + .version__dropdown--item { + align-items: center; + display: flex; + flex-wrap: nowrap; + gap: 0.5rem; + justify-content: space-between; + padding-inline: 1rem; + text-align: left; + } +} + +.version__tag { + border-radius: 4px; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.02em; + line-height: 1; + padding: 3px 5px; + text-transform: uppercase; +} + +.version__tag--unsupported { + background-color: #f1f2f4; + color: #5a6072; +} + +.version__tag--lts { + background-color: #f3faec; + color: #669438; +} + +/* Notice on docs for versions past their EOL date. The `.docs` ancestor is + * present in both the current and legacy layouts, and matches the specificity + * of the blockquote rules in css/styles.css and css/docs.css — this file wins + * the tie because it's the last stylesheet in the head. */ +.docs blockquote.version-unsupported-notice { + background-color: #fef9e6; + border: 1px solid #f7be08; + border-radius: 0.625rem; + color: #4c5773; + font-size: 14px; + line-height: 1.5; + margin: 1.5rem 0; + padding: 0.75rem 1rem; +} + +.docs blockquote.version-unsupported-notice a { + display: inline; +}