From ac2a2befb2ffdaa7b5da7e11d51eacc5126a58bf Mon Sep 17 00:00:00 2001
From: Janus Troelsen <ysangkok@gmail.com>
Date: Thu, 23 Jan 2025 09:37:40 -0600
Subject: [PATCH] feat(datasource/hackage): Detect deprecation status (#33778)

---
 lib/modules/datasource/hackage/index.spec.ts | 12 +++++++++---
 lib/modules/datasource/hackage/index.ts      | 16 ++++++++++------
 lib/modules/datasource/hackage/schema.ts     |  7 ++++++-
 3 files changed, 25 insertions(+), 10 deletions(-)

diff --git a/lib/modules/datasource/hackage/index.spec.ts b/lib/modules/datasource/hackage/index.spec.ts
index 676e082583..1235b179a9 100644
--- a/lib/modules/datasource/hackage/index.spec.ts
+++ b/lib/modules/datasource/hackage/index.spec.ts
@@ -8,7 +8,7 @@ describe('modules/datasource/hackage/index', () => {
   describe('versionToRelease', () => {
     it('should make release with given version', () => {
       expect(
-        versionToRelease('3.1.0', 'base', 'http://localhost').version,
+        versionToRelease('3.1.0', 'base', 'http://localhost', false).version,
       ).toBe('3.1.0');
     });
   });
@@ -33,11 +33,11 @@ describe('modules/datasource/hackage/index', () => {
       ).toBeNull();
     });
 
-    it('returns release for 200', async () => {
+    it('returns releases for 200', async () => {
       httpMock
         .scope(baseUrl)
         .get('/package/base.json')
-        .reply(200, { '4.20.0.1': 'normal' });
+        .reply(200, { '4.19.0.1': 'deprecated', '4.20.0.1': 'normal' });
       expect(
         await getPkgReleases({
           datasource: HackageDatasource.id,
@@ -46,9 +46,15 @@ describe('modules/datasource/hackage/index', () => {
       ).toEqual({
         registryUrl: baseUrl,
         releases: [
+          {
+            changelogUrl: baseUrl + 'package/base-4.19.0.1/changelog',
+            version: '4.19.0.1',
+            isDeprecated: true,
+          },
           {
             changelogUrl: baseUrl + 'package/base-4.20.0.1/changelog',
             version: '4.20.0.1',
+            isDeprecated: false,
           },
         ],
       });
diff --git a/lib/modules/datasource/hackage/index.ts b/lib/modules/datasource/hackage/index.ts
index 4a75568d48..481d92b56d 100644
--- a/lib/modules/datasource/hackage/index.ts
+++ b/lib/modules/datasource/hackage/index.ts
@@ -28,12 +28,14 @@ export class HackageDatasource extends Datasource {
       `${massagedPackageName}.json`,
     );
     const res = await this.http.getJson(url, HackagePackageMetadata);
-    const keys = Object.keys(res.body);
-    return {
-      releases: keys.map((version) =>
-        versionToRelease(version, packageName, registryUrl),
-      ),
-    };
+    const releases = [];
+    for (const [version, versionStatus] of Object.entries(res.body)) {
+      const isDeprecated = versionStatus === 'deprecated';
+      releases.push(
+        versionToRelease(version, packageName, registryUrl, isDeprecated),
+      );
+    }
+    return { releases };
   }
 }
 
@@ -41,6 +43,7 @@ export function versionToRelease(
   version: string,
   packageName: string,
   registryUrl: string,
+  isDeprecated: boolean,
 ): Release {
   return {
     version,
@@ -50,5 +53,6 @@ export function versionToRelease(
       `${packageName}-${version}`,
       'changelog',
     ),
+    isDeprecated,
   };
 }
diff --git a/lib/modules/datasource/hackage/schema.ts b/lib/modules/datasource/hackage/schema.ts
index dcee186743..4081d153f3 100644
--- a/lib/modules/datasource/hackage/schema.ts
+++ b/lib/modules/datasource/hackage/schema.ts
@@ -1,3 +1,8 @@
 import { z } from 'zod';
 
-export const HackagePackageMetadata = z.record(z.string());
+// See https://github.com/haskell/hackage-server
+// revision e885d36c
+// src/Distribution/Server/Features/PackageInfoJSON/State.hs line 160
+const VersionStatus = z.enum(['normal', 'deprecated', 'unpreferred']);
+
+export const HackagePackageMetadata = z.record(VersionStatus);
-- 
GitLab