From 00407c0050099b99d1da4df814c3e93ab4ccdc6e Mon Sep 17 00:00:00 2001 From: Lachlan Collins <1667261+lachlancollins@users.noreply.github.com> Date: Sat, 28 Mar 2026 18:03:36 +1100 Subject: [PATCH 1/3] WIP: github app webhook --- package.json | 3 + pnpm-lock.yaml | 243 +++++++++++++++++++++++++++++++ src/routeTree.gen.ts | 21 +++ src/routes/api/github/webhook.ts | 61 ++++++++ 4 files changed, 328 insertions(+) create mode 100644 src/routes/api/github/webhook.ts diff --git a/package.json b/package.json index dd152868..6e1c8e12 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,10 @@ "@netlify/vite-plugin-tanstack-start": "^1.0.2", "@number-flow/react": "^0.4.1", "@observablehq/plot": "^0.6.17", + "@octokit/auth-app": "^8.2.0", "@octokit/graphql": "^7.0.2", + "@octokit/rest": "^22.0.1", + "@octokit/webhooks": "^14.2.0", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.12", "@radix-ui/react-toast": "^1.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dd4063cd..b63c7c97 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,9 +44,18 @@ importers: '@observablehq/plot': specifier: ^0.6.17 version: 0.6.17 + '@octokit/auth-app': + specifier: ^8.2.0 + version: 8.2.0 '@octokit/graphql': specifier: ^7.0.2 version: 7.0.2 + '@octokit/rest': + specifier: ^22.0.1 + version: 22.0.1 + '@octokit/webhooks': + specifier: ^14.2.0 + version: 14.2.0 '@radix-ui/react-dialog': specifier: ^1.1.15 version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -1923,6 +1932,34 @@ packages: resolution: {integrity: sha512-/qaXP/7mc4MUS0s4cPPFASDRjtsWp85/TbfsciqDgU1HwYixbSbbytNuInD8AcTYC3xaxACgVX06agdfQy9W+g==} engines: {node: '>=12'} + '@octokit/auth-app@8.2.0': + resolution: {integrity: sha512-vVjdtQQwomrZ4V46B9LaCsxsySxGoHsyw6IYBov/TqJVROrlYdyNgw5q6tQbB7KZt53v1l1W53RiqTvpzL907g==} + engines: {node: '>= 20'} + + '@octokit/auth-oauth-app@9.0.3': + resolution: {integrity: sha512-+yoFQquaF8OxJSxTb7rnytBIC2ZLbLqA/yb71I4ZXT9+Slw4TziV9j/kyGhUFRRTF2+7WlnIWsePZCWHs+OGjg==} + engines: {node: '>= 20'} + + '@octokit/auth-oauth-device@8.0.3': + resolution: {integrity: sha512-zh2W0mKKMh/VWZhSqlaCzY7qFyrgd9oTWmTmHaXnHNeQRCZr/CXy2jCgHo4e4dJVTiuxP5dLa0YM5p5QVhJHbw==} + engines: {node: '>= 20'} + + '@octokit/auth-oauth-user@6.0.2': + resolution: {integrity: sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A==} + engines: {node: '>= 20'} + + '@octokit/auth-token@6.0.0': + resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} + engines: {node: '>= 20'} + + '@octokit/core@7.0.6': + resolution: {integrity: sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==} + engines: {node: '>= 20'} + + '@octokit/endpoint@11.0.3': + resolution: {integrity: sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==} + engines: {node: '>= 20'} + '@octokit/endpoint@9.0.2': resolution: {integrity: sha512-qhKW8YLIi+Kmc92FQUFGr++DYtkx/1fBv+Thua6baqnjnOsgBYJDCvWZR1YcINuHGOEQt416WOfE+A/oG60NBQ==} engines: {node: '>= 18'} @@ -1931,20 +1968,79 @@ packages: resolution: {integrity: sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==} engines: {node: '>= 18'} + '@octokit/graphql@9.0.3': + resolution: {integrity: sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==} + engines: {node: '>= 20'} + + '@octokit/oauth-authorization-url@8.0.0': + resolution: {integrity: sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ==} + engines: {node: '>= 20'} + + '@octokit/oauth-methods@6.0.2': + resolution: {integrity: sha512-HiNOO3MqLxlt5Da5bZbLV8Zarnphi4y9XehrbaFMkcoJ+FL7sMxH/UlUsCVxpddVu4qvNDrBdaTVE2o4ITK8ng==} + engines: {node: '>= 20'} + '@octokit/openapi-types@19.0.2': resolution: {integrity: sha512-8li32fUDUeml/ACRp/njCWTsk5t17cfTM1jp9n08pBrqs5cDFJubtjsSnuz56r5Tad6jdEPJld7LxNp9dNcyjQ==} + '@octokit/openapi-types@27.0.0': + resolution: {integrity: sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==} + + '@octokit/openapi-webhooks-types@12.1.0': + resolution: {integrity: sha512-WiuzhOsiOvb7W3Pvmhf8d2C6qaLHXrWiLBP4nJ/4kydu+wpagV5Fkz9RfQwV2afYzv3PB+3xYgp4mAdNGjDprA==} + + '@octokit/plugin-paginate-rest@14.0.0': + resolution: {integrity: sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-request-log@6.0.0': + resolution: {integrity: sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-rest-endpoint-methods@17.0.0': + resolution: {integrity: sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + '@octokit/request-error@5.0.1': resolution: {integrity: sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==} engines: {node: '>= 18'} + '@octokit/request-error@7.1.0': + resolution: {integrity: sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==} + engines: {node: '>= 20'} + + '@octokit/request@10.0.8': + resolution: {integrity: sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw==} + engines: {node: '>= 20'} + '@octokit/request@8.1.4': resolution: {integrity: sha512-M0aaFfpGPEKrg7XoA/gwgRvc9MSXHRO2Ioki1qrPDbl1e9YhjIwVoHE7HIKmv/m3idzldj//xBujcFNqGX6ENA==} engines: {node: '>= 18'} + '@octokit/rest@22.0.1': + resolution: {integrity: sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==} + engines: {node: '>= 20'} + '@octokit/types@12.1.1': resolution: {integrity: sha512-qnJTldJ1NyGT5MTsCg/Zi+y2IFHZ1Jo5+njNCjJ9FcainV7LjuHgmB697kA0g4MjZeDAJsM3B45iqCVsCLVFZg==} + '@octokit/types@16.0.0': + resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} + + '@octokit/webhooks-methods@6.0.0': + resolution: {integrity: sha512-MFlzzoDJVw/GcbfzVC1RLR36QqkTLUf79vLVO3D+xn7r0QgxnFoLZgtrzxiQErAjFUOdH6fas2KeQJ1yr/qaXQ==} + engines: {node: '>= 20'} + + '@octokit/webhooks@14.2.0': + resolution: {integrity: sha512-da6KbdNCV5sr1/txD896V+6W0iamFWrvVl8cHkBSPT+YlvmT3DwXa4jxZnQc+gnuTEqSWbBeoSZYTayXH9wXcw==} + engines: {node: '>= 20'} + '@oozcitak/dom@2.0.2': resolution: {integrity: sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w==} engines: {node: '>=20.0'} @@ -4045,6 +4141,9 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + before-after-hook@4.0.0: + resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} + better-ajv-errors@1.2.0: resolution: {integrity: sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==} engines: {node: '>= 12.13.0'} @@ -5183,6 +5282,9 @@ packages: resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} engines: {node: '>=8.0.0'} + fast-content-type-parse@3.0.0: + resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -6057,6 +6159,9 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json-with-bigint@3.5.8: + resolution: {integrity: sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==} + json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -7871,6 +7976,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} + toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} @@ -8053,9 +8162,15 @@ packages: unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + universal-github-app-jwt@2.2.2: + resolution: {integrity: sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==} + universal-user-agent@6.0.0: resolution: {integrity: sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==} + universal-user-agent@7.0.3: + resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} + unixify@1.0.0: resolution: {integrity: sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==} engines: {node: '>=0.10.0'} @@ -10123,6 +10238,57 @@ snapshots: interval-tree-1d: 1.0.4 isoformat: 0.2.1 + '@octokit/auth-app@8.2.0': + dependencies: + '@octokit/auth-oauth-app': 9.0.3 + '@octokit/auth-oauth-user': 6.0.2 + '@octokit/request': 10.0.8 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + toad-cache: 3.7.0 + universal-github-app-jwt: 2.2.2 + universal-user-agent: 7.0.3 + + '@octokit/auth-oauth-app@9.0.3': + dependencies: + '@octokit/auth-oauth-device': 8.0.3 + '@octokit/auth-oauth-user': 6.0.2 + '@octokit/request': 10.0.8 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/auth-oauth-device@8.0.3': + dependencies: + '@octokit/oauth-methods': 6.0.2 + '@octokit/request': 10.0.8 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/auth-oauth-user@6.0.2': + dependencies: + '@octokit/auth-oauth-device': 8.0.3 + '@octokit/oauth-methods': 6.0.2 + '@octokit/request': 10.0.8 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/auth-token@6.0.0': {} + + '@octokit/core@7.0.6': + dependencies: + '@octokit/auth-token': 6.0.0 + '@octokit/graphql': 9.0.3 + '@octokit/request': 10.0.8 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + before-after-hook: 4.0.0 + universal-user-agent: 7.0.3 + + '@octokit/endpoint@11.0.3': + dependencies: + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + '@octokit/endpoint@9.0.2': dependencies: '@octokit/types': 12.1.1 @@ -10135,14 +10301,60 @@ snapshots: '@octokit/types': 12.1.1 universal-user-agent: 6.0.0 + '@octokit/graphql@9.0.3': + dependencies: + '@octokit/request': 10.0.8 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/oauth-authorization-url@8.0.0': {} + + '@octokit/oauth-methods@6.0.2': + dependencies: + '@octokit/oauth-authorization-url': 8.0.0 + '@octokit/request': 10.0.8 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + '@octokit/openapi-types@19.0.2': {} + '@octokit/openapi-types@27.0.0': {} + + '@octokit/openapi-webhooks-types@12.1.0': {} + + '@octokit/plugin-paginate-rest@14.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 + + '@octokit/plugin-request-log@6.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + + '@octokit/plugin-rest-endpoint-methods@17.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 + '@octokit/request-error@5.0.1': dependencies: '@octokit/types': 12.1.1 deprecation: 2.3.1 once: 1.4.0 + '@octokit/request-error@7.1.0': + dependencies: + '@octokit/types': 16.0.0 + + '@octokit/request@10.0.8': + dependencies: + '@octokit/endpoint': 11.0.3 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + fast-content-type-parse: 3.0.0 + json-with-bigint: 3.5.8 + universal-user-agent: 7.0.3 + '@octokit/request@8.1.4': dependencies: '@octokit/endpoint': 9.0.2 @@ -10151,10 +10363,29 @@ snapshots: is-plain-object: 5.0.0 universal-user-agent: 6.0.0 + '@octokit/rest@22.0.1': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) + '@octokit/plugin-request-log': 6.0.0(@octokit/core@7.0.6) + '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) + '@octokit/types@12.1.1': dependencies: '@octokit/openapi-types': 19.0.2 + '@octokit/types@16.0.0': + dependencies: + '@octokit/openapi-types': 27.0.0 + + '@octokit/webhooks-methods@6.0.0': {} + + '@octokit/webhooks@14.2.0': + dependencies: + '@octokit/openapi-webhooks-types': 12.1.0 + '@octokit/request-error': 7.1.0 + '@octokit/webhooks-methods': 6.0.0 + '@oozcitak/dom@2.0.2': dependencies: '@oozcitak/infra': 2.0.2 @@ -12534,6 +12765,8 @@ snapshots: base64-js@1.5.1: {} + before-after-hook@4.0.0: {} + better-ajv-errors@1.2.0(ajv@8.17.1): dependencies: '@babel/code-frame': 7.27.1 @@ -13928,6 +14161,8 @@ snapshots: dependencies: pure-rand: 6.1.0 + fast-content-type-parse@3.0.0: {} + fast-deep-equal@3.1.3: {} fast-fifo@1.3.2: {} @@ -14847,6 +15082,8 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + json-with-bigint@3.5.8: {} + json5@2.2.3: {} jsonpointer@5.0.1: {} @@ -17033,6 +17270,8 @@ snapshots: dependencies: is-number: 7.0.0 + toad-cache@3.7.0: {} + toidentifier@1.0.1: {} toml@3.0.0: {} @@ -17245,8 +17484,12 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 + universal-github-app-jwt@2.2.2: {} + universal-user-agent@6.0.0: {} + universal-user-agent@7.0.3: {} + unixify@1.0.0: dependencies: normalize-path: 2.1.1 diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 1e4d66cb..861a7d76 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -83,6 +83,7 @@ import { Route as ShowcaseEditIdRouteImport } from './routes/showcase/edit.$id' import { Route as IntentRegistryPackageNameRouteImport } from './routes/intent/registry/$packageName' import { Route as AuthProviderStartRouteImport } from './routes/auth/$provider/start' import { Route as ApiMcpSplatRouteImport } from './routes/api/mcp/$' +import { Route as ApiGithubWebhookRouteImport } from './routes/api/github/webhook' import { Route as ApiExampleDeployRouteImport } from './routes/api/example/deploy' import { Route as ApiDiscordInteractionsRouteImport } from './routes/api/discord/interactions' import { Route as ApiDataPartnersRouteImport } from './routes/api/data/partners' @@ -493,6 +494,11 @@ const ApiMcpSplatRoute = ApiMcpSplatRouteImport.update({ path: '/api/mcp/$', getParentRoute: () => rootRouteImport, } as any) +const ApiGithubWebhookRoute = ApiGithubWebhookRouteImport.update({ + id: '/api/github/webhook', + path: '/api/github/webhook', + getParentRoute: () => rootRouteImport, +} as any) const ApiExampleDeployRoute = ApiExampleDeployRouteImport.update({ id: '/api/example/deploy', path: '/api/example/deploy', @@ -779,6 +785,7 @@ export interface FileRoutesByFullPath { '/api/data/partners': typeof ApiDataPartnersRoute '/api/discord/interactions': typeof ApiDiscordInteractionsRoute '/api/example/deploy': typeof ApiExampleDeployRoute + '/api/github/webhook': typeof ApiGithubWebhookRoute '/api/mcp/$': typeof ApiMcpSplatRoute '/auth/$provider/start': typeof AuthProviderStartRoute '/intent/registry/$packageName': typeof IntentRegistryPackageNameRouteWithChildren @@ -885,6 +892,7 @@ export interface FileRoutesByTo { '/api/data/partners': typeof ApiDataPartnersRoute '/api/discord/interactions': typeof ApiDiscordInteractionsRoute '/api/example/deploy': typeof ApiExampleDeployRoute + '/api/github/webhook': typeof ApiGithubWebhookRoute '/api/mcp/$': typeof ApiMcpSplatRoute '/auth/$provider/start': typeof AuthProviderStartRoute '/showcase/edit/$id': typeof ShowcaseEditIdRoute @@ -998,6 +1006,7 @@ export interface FileRoutesById { '/api/data/partners': typeof ApiDataPartnersRoute '/api/discord/interactions': typeof ApiDiscordInteractionsRoute '/api/example/deploy': typeof ApiExampleDeployRoute + '/api/github/webhook': typeof ApiGithubWebhookRoute '/api/mcp/$': typeof ApiMcpSplatRoute '/auth/$provider/start': typeof AuthProviderStartRoute '/intent/registry/$packageName': typeof IntentRegistryPackageNameRouteWithChildren @@ -1113,6 +1122,7 @@ export interface FileRouteTypes { | '/api/data/partners' | '/api/discord/interactions' | '/api/example/deploy' + | '/api/github/webhook' | '/api/mcp/$' | '/auth/$provider/start' | '/intent/registry/$packageName' @@ -1219,6 +1229,7 @@ export interface FileRouteTypes { | '/api/data/partners' | '/api/discord/interactions' | '/api/example/deploy' + | '/api/github/webhook' | '/api/mcp/$' | '/auth/$provider/start' | '/showcase/edit/$id' @@ -1331,6 +1342,7 @@ export interface FileRouteTypes { | '/api/data/partners' | '/api/discord/interactions' | '/api/example/deploy' + | '/api/github/webhook' | '/api/mcp/$' | '/auth/$provider/start' | '/intent/registry/$packageName' @@ -1422,6 +1434,7 @@ export interface RootRouteChildren { ApiDataPartnersRoute: typeof ApiDataPartnersRoute ApiDiscordInteractionsRoute: typeof ApiDiscordInteractionsRoute ApiExampleDeployRoute: typeof ApiExampleDeployRoute + ApiGithubWebhookRoute: typeof ApiGithubWebhookRoute ApiMcpSplatRoute: typeof ApiMcpSplatRoute AuthProviderStartRoute: typeof AuthProviderStartRoute IntentRegistryPackageNameRoute: typeof IntentRegistryPackageNameRouteWithChildren @@ -1957,6 +1970,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ApiMcpSplatRouteImport parentRoute: typeof rootRouteImport } + '/api/github/webhook': { + id: '/api/github/webhook' + path: '/api/github/webhook' + fullPath: '/api/github/webhook' + preLoaderRoute: typeof ApiGithubWebhookRouteImport + parentRoute: typeof rootRouteImport + } '/api/example/deploy': { id: '/api/example/deploy' path: '/api/example/deploy' @@ -2454,6 +2474,7 @@ const rootRouteChildren: RootRouteChildren = { ApiDataPartnersRoute: ApiDataPartnersRoute, ApiDiscordInteractionsRoute: ApiDiscordInteractionsRoute, ApiExampleDeployRoute: ApiExampleDeployRoute, + ApiGithubWebhookRoute: ApiGithubWebhookRoute, ApiMcpSplatRoute: ApiMcpSplatRoute, AuthProviderStartRoute: AuthProviderStartRoute, IntentRegistryPackageNameRoute: IntentRegistryPackageNameRouteWithChildren, diff --git a/src/routes/api/github/webhook.ts b/src/routes/api/github/webhook.ts new file mode 100644 index 00000000..723915c7 --- /dev/null +++ b/src/routes/api/github/webhook.ts @@ -0,0 +1,61 @@ +import { createFileRoute } from '@tanstack/react-router' +import { Webhooks } from '@octokit/webhooks' +import { Octokit } from '@octokit/rest' +import { createAppAuth } from '@octokit/auth-app' + +const webhooks = new Webhooks({ + secret: process.env.GITHUB_WEBHOOK_SECRET ?? 'test', +}) + +function getOctokit(installationId: number) { + return new Octokit({ + authStrategy: createAppAuth, + auth: { + appId: process.env.GITHUB_APP_ID!, + privateKey: process.env.GITHUB_PRIVATE_KEY!, + installationId, + }, + }) +} + +// Register event handlers +webhooks.on('pull_request.opened', async ({ payload }) => { + console.log('PR opened:', payload.pull_request.title) + + const octokit = getOctokit(payload.installation!.id) + + await octokit.issues.createComment({ + owner: payload.repository.owner.login, + repo: payload.repository.name, + issue_number: payload.pull_request.number, + body: `Thanks for opening this PR, @${payload.pull_request.user.login}! 👋`, + }) +}) + +webhooks.onError((error) => { + console.error('Webhook error:', error) +}) + +export const Route = createFileRoute('/api/github/webhook')({ + server: { + handlers: { + POST: async ({ request }) => { + const signature = request.headers.get('x-hub-signature-256') ?? '' + const body = await request.text() + + const isValid = await webhooks.verify(body, signature) + if (!isValid) { + return new Response('Invalid signature', { status: 401 }) + } + + await webhooks.receive({ + id: request.headers.get('x-github-delivery') ?? '', + name: request.headers.get('x-github-event') as any, + payload: JSON.parse(body), + }) + + return new Response('OK', { status: 200 }) + }, + }, + }, +}) From f8e1cce5ab8880ac7508fc5d6c766a7c2ce1a8e6 Mon Sep 17 00:00:00 2001 From: Lachlan Collins <1667261+lachlancollins@users.noreply.github.com> Date: Sat, 28 Mar 2026 18:11:43 +1100 Subject: [PATCH 2/3] remove @octokit/auth-app --- package.json | 1 - pnpm-lock.yaml | 81 -------------------------------- src/routes/api/github/webhook.ts | 15 ++---- src/utils/env.ts | 2 +- 4 files changed, 6 insertions(+), 93 deletions(-) diff --git a/package.json b/package.json index 6e1c8e12..d20beefa 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ "@netlify/vite-plugin-tanstack-start": "^1.0.2", "@number-flow/react": "^0.4.1", "@observablehq/plot": "^0.6.17", - "@octokit/auth-app": "^8.2.0", "@octokit/graphql": "^7.0.2", "@octokit/rest": "^22.0.1", "@octokit/webhooks": "^14.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b63c7c97..cb94b1e8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,9 +44,6 @@ importers: '@observablehq/plot': specifier: ^0.6.17 version: 0.6.17 - '@octokit/auth-app': - specifier: ^8.2.0 - version: 8.2.0 '@octokit/graphql': specifier: ^7.0.2 version: 7.0.2 @@ -1932,22 +1929,6 @@ packages: resolution: {integrity: sha512-/qaXP/7mc4MUS0s4cPPFASDRjtsWp85/TbfsciqDgU1HwYixbSbbytNuInD8AcTYC3xaxACgVX06agdfQy9W+g==} engines: {node: '>=12'} - '@octokit/auth-app@8.2.0': - resolution: {integrity: sha512-vVjdtQQwomrZ4V46B9LaCsxsySxGoHsyw6IYBov/TqJVROrlYdyNgw5q6tQbB7KZt53v1l1W53RiqTvpzL907g==} - engines: {node: '>= 20'} - - '@octokit/auth-oauth-app@9.0.3': - resolution: {integrity: sha512-+yoFQquaF8OxJSxTb7rnytBIC2ZLbLqA/yb71I4ZXT9+Slw4TziV9j/kyGhUFRRTF2+7WlnIWsePZCWHs+OGjg==} - engines: {node: '>= 20'} - - '@octokit/auth-oauth-device@8.0.3': - resolution: {integrity: sha512-zh2W0mKKMh/VWZhSqlaCzY7qFyrgd9oTWmTmHaXnHNeQRCZr/CXy2jCgHo4e4dJVTiuxP5dLa0YM5p5QVhJHbw==} - engines: {node: '>= 20'} - - '@octokit/auth-oauth-user@6.0.2': - resolution: {integrity: sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A==} - engines: {node: '>= 20'} - '@octokit/auth-token@6.0.0': resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} engines: {node: '>= 20'} @@ -1972,14 +1953,6 @@ packages: resolution: {integrity: sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==} engines: {node: '>= 20'} - '@octokit/oauth-authorization-url@8.0.0': - resolution: {integrity: sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ==} - engines: {node: '>= 20'} - - '@octokit/oauth-methods@6.0.2': - resolution: {integrity: sha512-HiNOO3MqLxlt5Da5bZbLV8Zarnphi4y9XehrbaFMkcoJ+FL7sMxH/UlUsCVxpddVu4qvNDrBdaTVE2o4ITK8ng==} - engines: {node: '>= 20'} - '@octokit/openapi-types@19.0.2': resolution: {integrity: sha512-8li32fUDUeml/ACRp/njCWTsk5t17cfTM1jp9n08pBrqs5cDFJubtjsSnuz56r5Tad6jdEPJld7LxNp9dNcyjQ==} @@ -7976,10 +7949,6 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - toad-cache@3.7.0: - resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} - engines: {node: '>=12'} - toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} @@ -8162,9 +8131,6 @@ packages: unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} - universal-github-app-jwt@2.2.2: - resolution: {integrity: sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==} - universal-user-agent@6.0.0: resolution: {integrity: sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==} @@ -10238,40 +10204,6 @@ snapshots: interval-tree-1d: 1.0.4 isoformat: 0.2.1 - '@octokit/auth-app@8.2.0': - dependencies: - '@octokit/auth-oauth-app': 9.0.3 - '@octokit/auth-oauth-user': 6.0.2 - '@octokit/request': 10.0.8 - '@octokit/request-error': 7.1.0 - '@octokit/types': 16.0.0 - toad-cache: 3.7.0 - universal-github-app-jwt: 2.2.2 - universal-user-agent: 7.0.3 - - '@octokit/auth-oauth-app@9.0.3': - dependencies: - '@octokit/auth-oauth-device': 8.0.3 - '@octokit/auth-oauth-user': 6.0.2 - '@octokit/request': 10.0.8 - '@octokit/types': 16.0.0 - universal-user-agent: 7.0.3 - - '@octokit/auth-oauth-device@8.0.3': - dependencies: - '@octokit/oauth-methods': 6.0.2 - '@octokit/request': 10.0.8 - '@octokit/types': 16.0.0 - universal-user-agent: 7.0.3 - - '@octokit/auth-oauth-user@6.0.2': - dependencies: - '@octokit/auth-oauth-device': 8.0.3 - '@octokit/oauth-methods': 6.0.2 - '@octokit/request': 10.0.8 - '@octokit/types': 16.0.0 - universal-user-agent: 7.0.3 - '@octokit/auth-token@6.0.0': {} '@octokit/core@7.0.6': @@ -10307,15 +10239,6 @@ snapshots: '@octokit/types': 16.0.0 universal-user-agent: 7.0.3 - '@octokit/oauth-authorization-url@8.0.0': {} - - '@octokit/oauth-methods@6.0.2': - dependencies: - '@octokit/oauth-authorization-url': 8.0.0 - '@octokit/request': 10.0.8 - '@octokit/request-error': 7.1.0 - '@octokit/types': 16.0.0 - '@octokit/openapi-types@19.0.2': {} '@octokit/openapi-types@27.0.0': {} @@ -17270,8 +17193,6 @@ snapshots: dependencies: is-number: 7.0.0 - toad-cache@3.7.0: {} - toidentifier@1.0.1: {} toml@3.0.0: {} @@ -17484,8 +17405,6 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 - universal-github-app-jwt@2.2.2: {} - universal-user-agent@6.0.0: {} universal-user-agent@7.0.3: {} diff --git a/src/routes/api/github/webhook.ts b/src/routes/api/github/webhook.ts index 723915c7..a937d342 100644 --- a/src/routes/api/github/webhook.ts +++ b/src/routes/api/github/webhook.ts @@ -1,20 +1,15 @@ import { createFileRoute } from '@tanstack/react-router' import { Webhooks } from '@octokit/webhooks' import { Octokit } from '@octokit/rest' -import { createAppAuth } from '@octokit/auth-app' +import { env } from '~/utils/env' const webhooks = new Webhooks({ - secret: process.env.GITHUB_WEBHOOK_SECRET ?? 'test', + secret: env.GITHUB_WEBHOOK_SECRET, }) -function getOctokit(installationId: number) { +function getOctokit() { return new Octokit({ - authStrategy: createAppAuth, - auth: { - appId: process.env.GITHUB_APP_ID!, - privateKey: process.env.GITHUB_PRIVATE_KEY!, - installationId, - }, + auth: env.GITHUB_AUTH_TOKEN }) } @@ -22,7 +17,7 @@ function getOctokit(installationId: number) { webhooks.on('pull_request.opened', async ({ payload }) => { console.log('PR opened:', payload.pull_request.title) - const octokit = getOctokit(payload.installation!.id) + const octokit = getOctokit() await octokit.issues.createComment({ owner: payload.repository.owner.login, diff --git a/src/utils/env.ts b/src/utils/env.ts index 9a0e9214..e62d6316 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -3,7 +3,7 @@ import * as v from 'valibot' // Define server-only schema const serverEnvSchema = v.object({ GITHUB_AUTH_TOKEN: v.optional(v.string(), 'USE_A_REAL_KEY_IN_PRODUCTION'), - GITHUB_WEBHOOK_SECRET: v.optional(v.string()), + GITHUB_WEBHOOK_SECRET: v.optional(v.string(), 'USE_A_REAL_KEY_IN_PRODUCTION'), GITHUB_OAUTH_CLIENT_ID: v.optional(v.string()), GITHUB_OAUTH_CLIENT_SECRET: v.optional(v.string()), GOOGLE_OAUTH_CLIENT_ID: v.optional(v.string()), From 8348ebf46885d485cd962323f04a70b907569716 Mon Sep 17 00:00:00 2001 From: Lachlan Collins <1667261+lachlancollins@users.noreply.github.com> Date: Sun, 29 Mar 2026 23:10:14 +1100 Subject: [PATCH 3/3] More webhook work --- package.json | 3 + pnpm-lock.yaml | 254 +++++++++++++++++++++++++++++-- src/github/changesetPreview.ts | 187 +++++++++++++++++++++++ src/routes/api/github/webhook.ts | 14 +- 4 files changed, 440 insertions(+), 18 deletions(-) create mode 100644 src/github/changesetPreview.ts diff --git a/package.json b/package.json index d20beefa..9c9a6da0 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,9 @@ }, "dependencies": { "@auth/core": "0.37.0", + "@changesets/assemble-release-plan": "^6.0.9", + "@changesets/config": "^3.1.3", + "@changesets/parse": "^0.4.3", "@floating-ui/react": "^0.27.8", "@hono/mcp": "^0.2.3", "@modelcontextprotocol/sdk": "^1.25.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cb94b1e8..b3567488 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,15 @@ importers: '@auth/core': specifier: 0.37.0 version: 0.37.0 + '@changesets/assemble-release-plan': + specifier: ^6.0.9 + version: 6.0.9 + '@changesets/config': + specifier: ^3.1.3 + version: 3.1.3 + '@changesets/parse': + specifier: ^0.4.3 + version: 0.4.3 '@floating-ui/react': specifier: ^0.27.8 version: 0.27.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -655,6 +664,33 @@ packages: '@braintree/sanitize-url@7.1.1': resolution: {integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==} + '@changesets/assemble-release-plan@6.0.9': + resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==} + + '@changesets/config@3.1.3': + resolution: {integrity: sha512-vnXjcey8YgBn2L1OPWd3ORs0bGC4LoYcK/ubpgvzNVr53JXV5GiTVj7fWdMRsoKUH7hhhMAQnsJUqLr21EncNw==} + + '@changesets/errors@0.2.0': + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + + '@changesets/get-dependents-graph@2.1.3': + resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} + + '@changesets/logger@0.1.1': + resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} + + '@changesets/parse@0.4.3': + resolution: {integrity: sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==} + + '@changesets/should-skip-package@0.1.2': + resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} + + '@changesets/types@4.1.0': + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + + '@changesets/types@6.1.0': + resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} + '@chevrotain/cst-dts-gen@11.0.3': resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==} @@ -1726,6 +1762,12 @@ packages: '@juggle/resize-observer@3.4.0': resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} + '@manypkg/find-root@1.1.0': + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + + '@manypkg/get-packages@1.1.3': + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + '@mapbox/node-pre-gyp@2.0.0': resolution: {integrity: sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==} engines: {node: '>=18'} @@ -3619,6 +3661,9 @@ packages: '@types/mysql@2.15.27': resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==} + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + '@types/node@22.19.3': resolution: {integrity: sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==} @@ -3993,6 +4038,10 @@ packages: resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} engines: {node: '>= 0.4'} + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + array.prototype.findlast@1.2.5: resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} @@ -4815,6 +4864,10 @@ packages: resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} engines: {node: '>=0.3.1'} + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + discord-interactions@4.4.0: resolution: {integrity: sha512-jjJx8iwAeJcj8oEauV43fue9lNqkf38fy60aSs2+G8D1nJmDxUIrk08o3h0F3wgwuBWWJUZO+X/VgfXsxpCiJA==} engines: {node: '>=18.4.0'} @@ -5246,6 +5299,9 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + extract-zip@2.0.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} @@ -5348,6 +5404,10 @@ packages: resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} engines: {node: '>=18'} + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -5395,6 +5455,14 @@ packages: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -5515,6 +5583,10 @@ packages: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} @@ -6140,6 +6212,9 @@ packages: engines: {node: '>=6'} hasBin: true + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jsonpointer@5.0.1: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} @@ -6309,6 +6384,10 @@ packages: resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} engines: {node: '>=14'} + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -6855,6 +6934,10 @@ packages: resolution: {integrity: sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==} engines: {node: '>=16.17'} + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -6867,6 +6950,10 @@ packages: resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==} engines: {node: '>=18'} + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} @@ -6887,6 +6974,10 @@ packages: resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} engines: {node: '>=14.16'} + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + p-wait-for@5.0.2: resolution: {integrity: sha512-lwx6u1CotQYPVju77R+D0vFomni/AqRfqLmqQ8hekklqZ6gAY9rONh7lBQ0uxWMkC2AuX9b2DVAl8To0NyP1JA==} engines: {node: '>=12'} @@ -7041,6 +7132,10 @@ packages: resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} engines: {node: '>=4'} + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + pkce-challenge@5.0.1: resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} engines: {node: '>=16.20.0'} @@ -7335,6 +7430,10 @@ packages: resolution: {integrity: sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==} engines: {node: '>=18'} + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -7564,11 +7663,6 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} - engines: {node: '>=10'} - hasBin: true - semver@7.7.3: resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} @@ -7663,6 +7757,10 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + slashes@3.0.12: resolution: {integrity: sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA==} @@ -8137,6 +8235,10 @@ packages: universal-user-agent@7.0.3: resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + unixify@1.0.0: resolution: {integrity: sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==} engines: {node: '>=0.10.0'} @@ -8914,8 +9016,7 @@ snapshots: '@babel/runtime@7.28.4': {} - '@babel/runtime@7.28.6': - optional: true + '@babel/runtime@7.28.6': {} '@babel/template@7.27.2': dependencies: @@ -8987,6 +9088,55 @@ snapshots: '@braintree/sanitize-url@7.1.1': {} + '@changesets/assemble-release-plan@6.0.9': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.3 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.7.3 + + '@changesets/config@3.1.3': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.3 + '@changesets/logger': 0.1.1 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.8 + + '@changesets/errors@0.2.0': + dependencies: + extendable-error: 0.1.7 + + '@changesets/get-dependents-graph@2.1.3': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + picocolors: 1.1.1 + semver: 7.7.3 + + '@changesets/logger@0.1.1': + dependencies: + picocolors: 1.1.1 + + '@changesets/parse@0.4.3': + dependencies: + '@changesets/types': 6.1.0 + js-yaml: 3.14.2 + + '@changesets/should-skip-package@0.1.2': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/types@4.1.0': {} + + '@changesets/types@6.1.0': {} + '@chevrotain/cst-dts-gen@11.0.3': dependencies: '@chevrotain/gast': 11.0.3 @@ -9769,6 +9919,22 @@ snapshots: '@juggle/resize-observer@3.4.0': {} + '@manypkg/find-root@1.1.0': + dependencies: + '@babel/runtime': 7.28.6 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + + '@manypkg/get-packages@1.1.3': + dependencies: + '@babel/runtime': 7.28.6 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + '@mapbox/node-pre-gyp@2.0.0': dependencies: consola: 3.4.2 @@ -9895,7 +10061,7 @@ snapshots: image-size: 2.0.2 js-image-generator: 1.0.4 parse-gitignore: 2.0.0 - semver: 7.7.2 + semver: 7.7.3 tmp-promise: 3.0.3 uuid: 11.1.0 write-file-atomic: 5.0.1 @@ -12056,6 +12222,8 @@ snapshots: dependencies: '@types/node': 24.3.0 + '@types/node@12.20.55': {} + '@types/node@22.19.3': dependencies: undici-types: 6.21.0 @@ -12549,6 +12717,8 @@ snapshots: is-string: 1.1.1 math-intrinsics: 1.1.0 + array-union@2.1.0: {} + array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.8 @@ -13430,6 +13600,10 @@ snapshots: diff@8.0.3: {} + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + discord-interactions@4.4.0: {} doctrine@2.1.0: @@ -14070,6 +14244,8 @@ snapshots: extend@3.0.2: {} + extendable-error@0.1.7: {} + extract-zip@2.0.1: dependencies: debug: 4.4.3 @@ -14170,6 +14346,11 @@ snapshots: find-up-simple@1.0.1: {} + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -14215,6 +14396,18 @@ snapshots: fresh@2.0.0: {} + fs-extra@7.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + fsevents@2.3.2: optional: true @@ -14343,6 +14536,15 @@ snapshots: define-properties: 1.2.1 gopd: 1.2.0 + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + globrex@0.1.2: {} glsl-noise@0.0.0: {} @@ -15009,6 +15211,10 @@ snapshots: json5@2.2.3: {} + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + jsonpointer@5.0.1: {} jsonwebtoken@9.0.2: @@ -15193,6 +15399,10 @@ snapshots: pkg-types: 2.3.0 quansync: 0.2.11 + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -15934,6 +16144,10 @@ snapshots: dependencies: p-timeout: 6.1.4 + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -15946,6 +16160,10 @@ snapshots: dependencies: yocto-queue: 1.2.1 + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + p-locate@5.0.0: dependencies: p-limit: 3.1.0 @@ -15964,6 +16182,8 @@ snapshots: p-timeout@6.1.4: {} + p-try@2.2.0: {} + p-wait-for@5.0.2: dependencies: p-timeout: 6.1.4 @@ -16051,8 +16271,7 @@ snapshots: dependencies: pify: 3.0.0 - path-type@4.0.0: - optional: true + path-type@4.0.0: {} path-type@6.0.0: {} @@ -16098,6 +16317,8 @@ snapshots: pify@3.0.0: {} + pify@4.0.1: {} + pkce-challenge@5.0.1: {} pkg-types@1.3.1: @@ -16381,6 +16602,13 @@ snapshots: type-fest: 4.41.0 unicorn-magic: 0.1.0 + read-yaml-file@1.1.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.2 + pify: 4.0.1 + strip-bom: 3.0.0 + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 @@ -16702,8 +16930,6 @@ snapshots: semver@6.3.1: {} - semver@7.7.2: {} - semver@7.7.3: {} send@1.2.1: @@ -16851,6 +17077,8 @@ snapshots: signal-exit@4.1.0: {} + slash@3.0.0: {} + slashes@3.0.12: {} source-map-explorer@2.5.3: @@ -17409,6 +17637,8 @@ snapshots: universal-user-agent@7.0.3: {} + universalify@0.1.2: {} + unixify@1.0.0: dependencies: normalize-path: 2.1.1 diff --git a/src/github/changesetPreview.ts b/src/github/changesetPreview.ts new file mode 100644 index 00000000..9f19652e --- /dev/null +++ b/src/github/changesetPreview.ts @@ -0,0 +1,187 @@ +import { writeFileSync } from 'node:fs' +import { dirname } from 'node:path' +import type { Octokit } from '@octokit/rest' +import assembleReleasePlan from '@changesets/assemble-release-plan' +import { parse as parseConfig } from '@changesets/config' +import parseChangeset from '@changesets/parse' + +const reasonRank = (reason: 'Changeset' | 'Dependent') => { + return reason === 'Changeset' ? 2 : 1 +} + +const fetchFile = async ({ owner, repo, ref, path, octokit }) => { + const response = await octokit.repos.getContent({ + owner, + repo, + ref, + path, + }) + return Buffer.from(response.data.content, 'base64').toString('utf-8') +} + +export const changesetPreview = async ({ + owner, + repo, + ref, + pull_number, + octokit, +}: { + owner: string + repo: string + ref: string + pull_number: number + octokit: Octokit +}) => { + const configFile = JSON.parse( + await fetchFile({ + owner, + repo, + ref, + path: '.changeset/config.json', + octokit, + }), + ) + + const changedFiles = await octokit.pulls.listFiles({ + repo, + owner, + pull_number, + }) + + const newChangesetPaths: Array = [] + + for (const file of changedFiles.data) { + if ( + file.filename.startsWith('.changeset/') && + file.filename.endsWith('.md') + ) { + console.log(`Found changeset file: ${file.filename}`) + newChangesetPaths.push(file.filename) + } + } + + const newChangesets = newChangesetPaths.map(async (url) => { + console.log(`Fetching changeset from ${url}`) + const content = await fetchFile({ owner, repo, ref, path: url, octokit }) + return parseChangeset(content) + }) + + const tree = await octokit.git.getTree({ + owner, + repo, + recursive: '1', + tree_sha: ref, + }) + + const packages: string[] = [] + + for (const item of tree.data.tree) { + if ( + item.path.startsWith('packages/') && + item.path.endsWith('/package.json') + ) { + packages.push(dirname(item.path)) + } + } + + const releasePlan = assembleReleasePlan( + newChangesets, + packages, + configFile, + undefined, + ) + const releases = releasePlan.releases + + if (releases.length === 0) { + const msg = + 'No changeset entries found. Merging this PR will not cause a version bump for any packages.\n' + process.stdout.write(msg) + if (values.output) { + writeFileSync(values.output, msg) + process.stdout.write(`Written to ${values.output}\n`) + } + return + } + + // 6. Diff + const bumps = [] + for (const release of releases) { + if (release.oldVersion === release.newVersion) continue + const reason = release.changesets.length !== 0 ? 'Changeset' : 'Dependent' + bumps.push({ ...release, reason }) + } + + // Order by reason and name + bumps.sort( + (a, b) => + reasonRank(b.reason) - reasonRank(a.reason) || + a.name.localeCompare(b.name), + ) + + // 7. Build markdown + const lines = [] + + if (bumps.length === 0) { + lines.push( + 'No version changes detected. Merging this PR will not cause a version bump for any packages.', + ) + } else { + const majorBumps = bumps.filter((b) => b.type === 'major') + const minorBumps = bumps.filter((b) => b.type === 'minor') + const patchBumps = bumps.filter((b) => b.type === 'patch') + const directBumps = bumps.filter((b) => b.reason === 'Changeset') + const indirectBumps = bumps.filter((b) => b.reason === 'Dependent') + + lines.push( + `**${directBumps.length}** package(s) bumped directly, **${indirectBumps.length}** bumped as dependents.`, + ) + lines.push('') + + if (majorBumps.length > 0) { + lines.push('### 🟥 Major bumps') + lines.push('') + lines.push('| Package | Version | Reason |') + lines.push('| --- | --- | --- |') + for (const b of majorBumps) { + lines.push( + `| \`${b.name}\` | ${b.oldVersion} → ${b.newVersion} | ${b.reason} |`, + ) + } + lines.push('') + } + + if (minorBumps.length > 0) { + lines.push('### 🟨 Minor bumps') + lines.push('') + lines.push('| Package | Version | Reason |') + lines.push('| --- | --- | --- |') + for (const b of minorBumps) { + lines.push( + `| \`${b.name}\` | ${b.oldVersion} → ${b.newVersion} | ${b.reason} |`, + ) + } + lines.push('') + } + + if (patchBumps.length > 0) { + lines.push('### 🟩 Patch bumps') + lines.push('') + lines.push('| Package | Version | Reason |') + lines.push('| --- | --- | --- |') + for (const b of patchBumps) { + lines.push( + `| \`${b.name}\` | ${b.oldVersion} → ${b.newVersion} | ${b.reason} |`, + ) + } + } + } + + lines.push('') + const md = lines.join('\n') + + process.stdout.write(md) + if (values.output) { + writeFileSync(values.output, md) + process.stdout.write(`Written to ${values.output}\n`) + } +} diff --git a/src/routes/api/github/webhook.ts b/src/routes/api/github/webhook.ts index a937d342..13640be7 100644 --- a/src/routes/api/github/webhook.ts +++ b/src/routes/api/github/webhook.ts @@ -2,6 +2,7 @@ import { createFileRoute } from '@tanstack/react-router' import { Webhooks } from '@octokit/webhooks' import { Octokit } from '@octokit/rest' import { env } from '~/utils/env' +import { changesetPreview } from "~/github/changesetPreview" const webhooks = new Webhooks({ secret: env.GITHUB_WEBHOOK_SECRET, @@ -14,16 +15,17 @@ function getOctokit() { } // Register event handlers -webhooks.on('pull_request.opened', async ({ payload }) => { +webhooks.on(['pull_request.opened', 'pull_request.synchronize'], async ({ payload }) => { console.log('PR opened:', payload.pull_request.title) const octokit = getOctokit() - await octokit.issues.createComment({ - owner: payload.repository.owner.login, - repo: payload.repository.name, - issue_number: payload.pull_request.number, - body: `Thanks for opening this PR, @${payload.pull_request.user.login}! 👋`, + await changesetPreview({ + owner: payload.pull_request.head.repo.owner!.login, + repo: payload.pull_request.head.repo.name, + ref: payload.pull_request.head.ref, + pull_number: payload.number, + octokit }) })