diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 092b236d7..940c71c09 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -22,7 +22,7 @@ const { version } = require('../package.json'); module.exports = { title: `${projectName}`, - tagline: `Deploy custom push protections and policies on top of Git`, + tagline: `Open source contribution. With your guardrails.`, url: 'https://git-proxy.finos.org', baseUrl: '/', favicon: 'img/favicon/favicon-finos.ico', diff --git a/website/src/components/avatar.js b/website/src/components/avatar.js index 330da117c..a6a54e3d3 100644 --- a/website/src/components/avatar.js +++ b/website/src/components/avatar.js @@ -35,7 +35,7 @@ export default function Avatar({ name, description, username }) { return (
- +
{name}
{description} diff --git a/website/src/css/custom.css b/website/src/css/custom.css index 76eb3240f..d1ab91403 100644 --- a/website/src/css/custom.css +++ b/website/src/css/custom.css @@ -181,3 +181,27 @@ padding: 0 10px; box-sizing: border-box; } + +.navbar-sidebar + .menu__link:is( + .header-read-doc-link, + .header-slack-link, + .header-star-link, + .header-fork-link, + .header-github-link + ) { + align-items: center; + gap: 12px; + min-height: 44px; +} + +.navbar-sidebar + .menu__link:is( + .header-read-doc-link, + .header-slack-link, + .header-star-link, + .header-fork-link, + .header-github-link + )::after { + content: attr(aria-label); +} diff --git a/website/src/pages/homepage.module.css b/website/src/pages/homepage.module.css new file mode 100644 index 000000000..ba9c4daee --- /dev/null +++ b/website/src/pages/homepage.module.css @@ -0,0 +1,522 @@ +/** + * Copyright 2026 GitProxy Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.home { + --home-accent: #0878b8; + --home-muted: #536275; + --home-line: #dce3ea; + --home-surface: #f3f6f9; + color: var(--ifm-font-color-base); +} + +:global([data-theme='dark']) .home { + --home-accent: #70c7f4; + --home-muted: #b2bfce; + --home-line: #344150; + --home-surface: #19212b; +} + +.hero { + background: #07131e; + color: #f5f9ff; + border-bottom: 3px solid #159bd7; +} + +.heroInner { + max-width: 1120px; + margin: 0 auto; + padding: 32px 28px 52px; + text-align: center; + animation: enter 550ms ease-out both; +} + +.logo { + display: block; + width: min(340px, 75%); + height: auto; + margin: 0 auto 4px; +} + +.hero h1 { + margin: 0 0 22px; + font-size: clamp(2rem, 4.5vw, 3.65rem); + line-height: 1.12; + letter-spacing: -0.045em; + font-weight: 750; +} + +.hero h1 span { + color: #75caff; +} + +.heroDescription { + max-width: 650px; + margin: 0 auto; + font-size: 1.15rem; + line-height: 1.65; + color: #c8d6e6; +} + +.actions { + display: flex; + justify-content: center; + align-items: center; + flex-wrap: wrap; + gap: 12px; + margin-top: 26px; +} + +.primaryAction, +.secondaryAction { + display: inline-flex; + justify-content: center; + align-items: center; + gap: 20px; + min-height: 48px; + padding: 12px 22px; + font-size: 0.95rem; + font-weight: 650; + border-radius: 4px; + transition: + background 180ms ease, + border-color 180ms ease; +} + +.primaryAction { + background: #79cfff; + color: #07131e; + border: 1px solid #79cfff; +} + +.primaryAction:hover { + background: #b6e6ff; + border-color: #b6e6ff; + color: #07131e; + text-decoration: none; +} + +.secondaryAction { + border: 1px solid #607489; + color: #f5f9ff; +} + +.secondaryAction:hover { + background: #162a3d; + border-color: #b6e6ff; + color: #fff; + text-decoration: none; +} + +.home a:focus-visible { + outline: 3px solid var(--home-accent); + outline-offset: 5px; +} + +.hero a:focus-visible { + outline-color: #fff; +} + +.projectNote { + margin: 24px 0 0; + color: #aebfd1; + font-size: 0.8rem; + letter-spacing: 0.035em; +} + +.section { + max-width: 1160px; + margin: 0 auto; + padding: 80px 32px; +} + +.sectionHeading { + max-width: 760px; +} + +.eyebrow { + margin-bottom: 14px; + color: var(--home-accent); + font-size: 0.8rem; + font-weight: 700; + letter-spacing: 0.095em; + text-transform: uppercase; +} + +.section h2 { + font-size: clamp(1.8rem, 3.1vw, 2.6rem); + letter-spacing: -0.035em; + line-height: 1.2; + margin-bottom: 22px; +} + +.section p { + line-height: 1.75; +} + +.sectionHeading > p:last-child, +.capability p, +.flowStep p, +.join p:last-child { + color: var(--home-muted); +} + +.capabilities { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + column-gap: 64px; + margin-top: 44px; +} + +.capability { + display: flex; + gap: 22px; + border-top: 1px solid var(--home-line); + padding: 30px 0; +} + +.number { + color: var(--home-accent); + font-size: 0.8rem; + font-variant-numeric: tabular-nums; + padding-top: 5px; +} + +.capability h3 { + font-size: 1.3rem; + margin-bottom: 12px; + letter-spacing: -0.02em; +} + +.capability p { + font-size: 0.98rem; + margin-bottom: 16px; +} + +.textLink { + display: inline-flex; + align-items: center; + gap: 12px; + color: var(--home-accent); + font-weight: 650; + line-height: 1.6; +} + +.textLink span { + transition: transform 180ms ease; +} + +.textLink:hover span { + transform: translateX(4px); +} + +.workflow { + background: var(--home-surface); + border-block: 1px solid var(--home-line); +} + +.flow { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + list-style: none; + padding: 0; + margin: 42px 0 26px; +} + +.flowStep { + position: relative; + border-block: 1px solid var(--home-line); + padding: 26px; + margin: 0; +} + +.flowStep:not(:last-child)::after { + content: '→'; + position: absolute; + right: -10px; + top: 47%; + z-index: 1; + font-size: 1.5rem; + color: var(--home-accent); + background: var(--home-surface); + line-height: 1; +} + +.flowProxy { + border-block: 2px solid var(--home-accent); +} + +.flowLabel { + display: block; + margin-bottom: 20px; + color: var(--home-accent); + font-size: 0.8rem; + font-weight: 700; +} + +.flowStep h3 { + font-size: 1.1rem; + line-height: 1.4; + margin-bottom: 12px; +} + +.flowStep p { + font-size: 0.9rem; + margin: 0; +} + +.join { + display: flex; + justify-content: space-between; + align-items: center; + gap: 48px; +} + +.join > div:first-child { + max-width: 660px; +} + +.join h2 { + margin-bottom: 16px; +} + +.join p:last-child { + margin: 0; +} + +.joinLinks { + display: flex; + flex-direction: column; + flex-shrink: 0; + gap: 18px; +} + +.community { + padding-block: 32px 40px; +} + +.community h2 { + margin-bottom: 10px; +} + +.community > p { + color: var(--home-muted, var(--ifm-color-content-secondary)); +} + +.communityPosts { + max-width: 42rem; + border-block: 1px solid var(--home-line, var(--ifm-toc-border-color)); +} + +.communityPosts summary { + padding: 14px 0; + cursor: pointer; + color: var(--home-accent, var(--ifm-link-color)); + font-weight: 650; +} + +.communityPosts summary:focus-visible { + outline: 3px solid var(--home-accent, var(--ifm-link-color)); + outline-offset: 2px; +} + +.communityLinks { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr)); + gap: 0.75rem 1.5rem; + margin: 0 0 16px; +} + +.workflowDetails { + max-width: 42rem; + margin-bottom: 18px; + border-block: 1px solid var(--home-line); +} + +.workflowDetails summary { + padding: 12px 0; + cursor: pointer; + color: var(--home-accent); + font-weight: 650; +} + +.workflowDetails summary:focus-visible { + outline: 3px solid var(--home-accent); + outline-offset: 2px; +} + +.workflowDetails p { + margin-bottom: 16px; + color: var(--home-muted); +} + +.maintainers { + padding-block: 32px 48px; +} + +.maintainerList { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr)); + gap: 0 24px; + padding: 0; + margin: 0; + list-style: none; +} + +.maintainerList li { + border-top: 1px solid var(--home-line); + padding: 16px 8px; + transition: background-color 160ms ease; +} + +.maintainerList li:focus-within { + background-color: var(--ifm-hover-overlay); +} + +@media (hover: hover) { + .maintainerList li:hover { + background-color: var(--ifm-hover-overlay); + } +} + +.maintainerList :global(.avatar) { + flex-direction: row; + align-items: center; + gap: 14px; + margin-bottom: 0 !important; +} + +.maintainerList :global(.avatar__photo) { + width: 52px; + height: 52px; + flex-shrink: 0; + margin: 0 !important; +} + +.maintainerList :global(.avatar__intro) { + text-align: left; + margin: 0; + overflow-wrap: anywhere; +} + +@keyframes enter { + from { + opacity: 0; + transform: translateY(12px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@supports (animation-timeline: view()) { + .capability, + .flowStep, + .maintainerList li { + animation: enter linear both; + animation-timeline: view(); + animation-range: entry 0% entry 25%; + } +} + +@media (max-width: 767px) { + .heroInner { + padding: 20px 24px 36px; + } + .logo { + width: 270px; + margin-bottom: 8px; + } + .hero h1 { + font-size: clamp(1.8rem, 6vw, 2.7rem); + margin-bottom: 18px; + } + .heroDescription { + font-size: 1rem; + } + .actions { + gap: 10px; + margin-top: 22px; + } + .primaryAction, + .secondaryAction { + font-size: 0.9rem; + padding: 12px 16px; + } + .projectNote { + font-size: 0.72rem; + margin-top: 20px; + } + .section { + padding: 52px 24px; + } + .community, + .maintainers { + padding-block: 32px; + } + .capabilities { + grid-template-columns: 1fr; + margin-top: 30px; + } + .capability { + gap: 18px; + padding: 24px 0; + } + .flow { + grid-template-columns: 1fr; + gap: 20px; + margin-top: 30px; + } + .flowStep { + padding: 22px 0; + } + .flowStep:not(:last-child)::after { + content: '↓'; + right: 22px; + top: auto; + bottom: -20px; + } + .flowLabel { + margin-bottom: 12px; + } + .join { + flex-direction: column; + align-items: flex-start; + gap: 26px; + } + .joinLinks { + flex-direction: row; + flex-wrap: wrap; + gap: 16px 24px; + } +} + +@media (prefers-reduced-motion: reduce) { + .heroInner, + .capability, + .flowStep, + .maintainerList li { + animation: none; + } + .home a, + .maintainerList li, + .textLink span { + transition: none; + } + .textLink:hover span { + transform: none; + } +} diff --git a/website/src/pages/index.js b/website/src/pages/index.js index bc1994a1d..fc63919be 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -18,10 +18,47 @@ import React, { useEffect } from 'react'; import Layout from '@theme/Layout'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import Avatar from '../components/avatar'; -import TerminalDemo from '../components/terminalDemo'; +import Link from '@docusaurus/Link'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import styles from './homepage.module.css'; import Testimonials from './testimonials'; import axios from 'axios'; +const capabilities = [ + { + number: '01', + title: 'Inspect', + description: + 'Analyse pushes before they leave your organisation, including commits, authors, diffs and secrets. Built-in processors include hidden-commit detection and Gitleaks secret scanning.', + to: '/docs/architecture/processors', + link: 'Explore the checks', + }, + { + number: '02', + title: 'Enforce', + description: + 'Apply repository access rules, commit policies, pre-receive hooks and approval workflows at the point of push.', + to: '/docs/configuration/reference', + link: 'Configure your policies', + }, + { + number: '03', + title: 'Extend', + description: + 'Add organisation-specific controls using configurable plugins, distributed as standard npm packages or loaded as JavaScript modules.', + to: '/docs/development/plugins', + link: 'Build a plugin', + }, + { + number: '04', + title: 'Work across Git platforms', + description: + 'Proxy GitHub, GitLab and other Git hosts from one instance. GitProxy v2 supports multiple hosts and Git operations over HTTP/HTTPS or SSH.', + to: '/docs/upgrading-to-v2', + link: 'Discover multi-host support', + }, +]; + /** * Home page component * @return {JSX.Element} @@ -29,225 +66,227 @@ import axios from 'axios'; function Home() { const context = useDocusaurusContext(); const { siteConfig = {} } = context; + const logoUrl = useBaseUrl('/img/git-proxy.png'); - const [showDemo, setShowDemo] = React.useState(false); const [contributors, setContributors] = React.useState([]); useEffect(() => { - axios.get('https://api.github.com/repos/finos/git-proxy/contributors').then((res) => { - setContributors(res.data); - }); + axios + .get('https://api.github.com/repos/finos/git-proxy/contributors') + .then(({ data }) => { + if (Array.isArray(data)) setContributors(data); + }) + .catch(() => { + // The GitHub contributors link remains available if the API cannot load. + }); }, []); return ( -
-
-

GitProxy v{siteConfig.customFields.version} is out! 🥳

-
+
+
+
+ GitProxy +

+ Open source contribution. +
+ With your guardrails. +

+

+ An open source policy and control layer between developers and Git hosting platforms. +

+
+ + Get started + + + Read the documentation + +
+

Open source · Apache 2.0 · A FINOS project

+
+
+ +
+
+

Built around your workflow

+

Keep Git. Add the controls you need.

- GitProxy deploys custom push protections and policies on top of Git. It is a highly - configurable framework allowing developers and organizations to enforce push - protections relevant to their developer workflow, security posture and risk appetite. + GitProxy intercepts Git pushes and applies configurable checks and workflows before + changes reach the remote repository. Combine the familiar open source contribution + experience with your organisation’s security, legal and compliance requirements.

+
+
+ {capabilities.map(({ number, title, description, to, link }) => ( +
+ +
+

{title}

+

{description}

+ + {link} + +
+
+ ))} +
+
+ +
+
+
+

Familiar Git. Configurable controls.

+

Security controls without changing how developers work.

+

+ Developers continue to branch, commit and push with Git. GitProxy sits in the path + to the remote Git service, evaluates the push, and either blocks it with feedback or + allows it through the configured workflow. +

+
+
    +
  1. + 01 / Developer +

    Branch. Commit. Push.

    +

    Use the Git tools and workflow you already know.

    +
  2. +
  3. + 02 / GitProxy +

    Your policies, at push.

    +

    Inspect changes, enforce rules and follow configured approvals.

    +
  4. +
  5. + 03 / Git hosting +

    Your remote repository.

    +

    Changes proceed when the configured checks and workflow allow them.

    +
  6. +
+
+ What happens when a push needs approval? +

+ GitProxy blocks the push and provides a link to request approval. Once the push is + approved, the developer can retry it using the same Git command. +

+
+ + Explore the processing chain + +
+
+ +
+
+

A FINOS project, built in the open

+

Try it. Use it. Help shape it.

- GitProxy is built with a developer-first mindset. By presenting simple-to-follow - remediation instructions in the CLI/Terminal, it minimises the friction of use and - adoption, and keeps developers focused on what matters: committing and pushing code. + Start with GitProxy, explore the code, or contribute a control your organisation + needs.

-
- - Read our Docs - - - - Quickstart 🚀 - + Contribute +
-
-
-
-
-
-
-
-
-
- {showDemo ? ( -
- -
- ) : ( -
- -
- )} -
-
- -
-

- Our{' '} - - Maintainers - -

-
-
-
- {' '} + + +
+

Our maintainers

+
    +
  • -
-
-
-
- {' '} + +
  • -
  • -
    -
    -
    - {' '} + +
  • -
  • -
    -
    -
    -
    -
    + +
  • -
  • -
    -
    -
    + +
  • -
  • -
    -
    -
    + +
  • -
  • -
    -
    -
    -
    -
    + +
  • -
  • -
    -
    -
    + +
  • -
  • -
    -
    -
    + +
  • -
  • -
    -
    -
    -
    -

    - Our{' '} - - Contributors - -

    -
    - {contributors.map((contributor) => { - if (![29139614, 49699333].includes(contributor.id)) - return ( -
    -
    - 1 ? 'contributions' : 'contribution'}`} - username={contributor.login} - /> -
    -
    - ); - })} -
    -
    + + + +
    +

    Our contributors

    +
      + {contributors + .filter(({ id }) => ![29139614, 49699333].includes(id)) + .map(({ id, login, contributions }) => ( +
    • + +
    • + ))} +
    + + View contributors on GitHub + +
    + ); } diff --git a/website/src/pages/testimonials.js b/website/src/pages/testimonials.js index f0ceaa192..51af4f6d5 100644 --- a/website/src/pages/testimonials.js +++ b/website/src/pages/testimonials.js @@ -16,69 +16,30 @@ import React from 'react'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import { LinkedInEmbed } from 'react-social-media-embed'; -import Slider from 'react-slick'; -import 'slick-carousel/slick/slick.css'; -import 'slick-carousel/slick/slick-theme.css'; +import styles from './homepage.module.css'; -/** - * Home page component - * @return {JSX.Element} - */ function Testimonials() { - const context = useDocusaurusContext(); - const { siteConfig = {} } = context; - const { posts } = siteConfig.customFields; - const settings = { - infinite: true, - speed: 500, - slidesToShow: 3, - slidesToScroll: 3, - swipeToSlide: true, - swipe: true, - responsive: [ - { - breakpoint: 1024, - settings: { - slidesToShow: 2, - slidesToScroll: 2, - infinite: true, - dots: true, - }, - }, - { - breakpoint: 800, - settings: { - slidesToShow: 1, - slidesToScroll: 1, - initialSlide: 1, - }, - }, - ], - dots: true, - arrows: true, - }; + const { siteConfig } = useDocusaurusContext(); + const posts = siteConfig.customFields.posts.filter((post) => post.platform === 'linkedin'); return ( -
    -
    -

    Testimonials 📣

    -
    - -
    - - {posts - .filter((post) => post.platform === 'linkedin') - .map((post) => { - return ( -
    - -
    - ); - })} -
    -
    -
    +
    +

    From the community

    +

    Explore posts about GitProxy shared by the community on LinkedIn.

    +
    + Browse LinkedIn posts ({posts.length}) + +
    +
    ); } diff --git a/website/static/img/git-proxy.png b/website/static/img/git-proxy.png new file mode 100644 index 000000000..f7df71b72 Binary files /dev/null and b/website/static/img/git-proxy.png differ