Repository: FHIR/sushi
Pull request #1551 (2024-12-31 01:59:12)

Use latest hl7.fhir.uv.tools release-specific package
**Description:** The IG Publisher now uses the "latest" release-specific version of `hl7.fhir.uv.tools` (e.g., `hl7.fhir.uv.tools.r4#0.3.0`) instead of a non-release-specific current build. This PR updates SUSHI to do the same. As a result, automatic dependency loading no longer uses any `current` builds, so it is more compatible with FSH Online.

**Testing Instructions:** Run this branch against an R4 IG and confirm it loads the most recent R4 tools package (currently 0.3.0). Also run against an R5 IG and confirm it loads the most recent R5 tools package (also 0.3.0).

I ran a full regression against master and there were no changes, so this change does not impact any current projects.

**Related Issue:** N/A



Patch:
diff --git a/src/utils/Processing.ts b/src/utils/Processing.ts
index 443641d..e774878 100644
--- a/src/utils/Processing.ts
+++ b/src/utils/Processing.ts
@@ -51,8 +51,14 @@ type FshFhirMapping = {
 // For some context on implicit packages, see: https://chat.fhir.org/#narrow/stream/179239-tooling/topic/New.20Implicit.20Package/near/325318949
 export const AUTOMATIC_DEPENDENCIES: AutomaticDependency[] = [
   {
-    packageId: 'hl7.fhir.uv.tools',
-    version: 'current'
+    packageId: 'hl7.fhir.uv.tools.r4',
+    version: 'latest',
+    fhirVersions: ['R4', 'R4B']
+  },
+  {
+    packageId: 'hl7.fhir.uv.tools.r5',
+    version: 'latest',
+    fhirVersions: ['R5', 'R6']
   },
   {
     packageId: 'hl7.terminology.r4',


Test patch:
diff --git a/test/testhelpers/asserts.ts b/test/testhelpers/asserts.ts
index e0431b5..695e754 100644
--- a/test/testhelpers/asserts.ts
+++ b/test/testhelpers/asserts.ts
@@ -309,6 +309,8 @@ export function assertAutomaticR4Dependencies(packages: string[]) {
       expect(packages).toContain('hl7.terminology.r4#9.9.9');
     } else if (dep.packageId === 'hl7.fhir.uv.extensions.r4' && dep.version === 'latest') {
       expect(packages).toContain('hl7.fhir.uv.extensions.r4#9.9.9');
+    } else if (dep.packageId === 'hl7.fhir.uv.tools.r4' && dep.version === 'latest') {
+      expect(packages).toContain('hl7.fhir.uv.tools.r4#9.9.9');
     } else if (!dep.packageId.endsWith('.r5')) {
       expect(packages).toContain(`${dep.packageId}#${dep.version}`);
     }
@@ -321,6 +323,8 @@ export function assertAutomaticR5Dependencies(packages: string[]) {
       expect(packages).toContain('hl7.terminology.r5#9.9.9');
     } else if (dep.packageId === 'hl7.fhir.uv.extensions.r5' && dep.version === 'latest') {
       expect(packages).toContain('hl7.fhir.uv.extensions.r5#9.9.9');
+    } else if (dep.packageId === 'hl7.fhir.uv.tools.r5' && dep.version === 'latest') {
+      expect(packages).toContain('hl7.fhir.uv.tools.r5#9.9.9');
     } else if (!dep.packageId.endsWith('.r4')) {
       expect(packages).toContain(`${dep.packageId}#${dep.version}`);
     }
diff --git a/test/utils/Processing.test.ts b/test/utils/Processing.test.ts
index 2c88b99..eb4b108 100644
--- a/test/utils/Processing.test.ts
+++ b/test/utils/Processing.test.ts
@@ -688,7 +688,7 @@ describe('Processing', () => {
         expect(loadedPackages).toHaveLength(2 + NUM_R4_AUTO_DEPENDENCIES);
         expect(loadedPackages).toEqual([
           'sushi-r5forR4#1.0.0',
-          'hl7.fhir.uv.tools#current',
+          'hl7.fhir.uv.tools.r4#9.9.9',
           'hl7.terminology.r4#9.9.9',
           'hl7.fhir.uv.extensions.r4#9.9.9',
           'hl7.fhir.us.core#3.1.0',
@@ -704,7 +704,7 @@ describe('Processing', () => {
         { packageId: 'hl7.fhir.us.core', version: '3.1.0' },
         { packageId: 'hl7.fhir.uv.extensions.r4', version: '7.7.7' },
         { packageId: 'hl7.terminology.r4', version: '8.8.8' },
-        { packageId: 'hl7.fhir.uv.tools', version: '9.9.9' }
+        { packageId: 'hl7.fhir.uv.tools.r4', version: '9.8.7' }
       ];
       const defs = await getTestFHIRDefinitions();
       return loadExternalDependencies(defs, usCoreDependencyConfig).then(() => {
@@ -715,7 +715,7 @@ describe('Processing', () => {
           'hl7.fhir.us.core#3.1.0',
           'hl7.fhir.uv.extensions.r4#7.7.7',
           'hl7.terminology.r4#8.8.8',
-          'hl7.fhir.uv.tools#9.9.9',
+          'hl7.fhir.uv.tools.r4#9.8.7',
           'hl7.fhir.r4.core#4.0.1'
         ]);
         expect(loggerSpy.getAllLogs('warn')).toHaveLength(0);
@@ -731,7 +731,7 @@ describe('Processing', () => {
         expect(loadedPackages).toHaveLength(1 + NUM_R4_AUTO_DEPENDENCIES);
         expect(loadedPackages).toContain('hl7.fhir.r4b.core#4.1.0');
         expect(loadedPackages).toContain('sushi-r5forR4#1.0.0');
-        expect(loadedPackages).toContain('hl7.fhir.uv.tools#current');
+        expect(loadedPackages).toContain('hl7.fhir.uv.tools.r4#9.9.9');
         expect(loadedPackages).toContain('hl7.terminology.r4#9.9.9');
         expect(loggerSpy.getLastMessage('warn')).toMatch(
           /support for pre-release versions of FHIR is experimental/s
@@ -748,7 +748,7 @@ describe('Processing', () => {
         expect(loadedPackages).toHaveLength(1 + NUM_R4_AUTO_DEPENDENCIES);
         expect(loadedPackages).toContain('hl7.fhir.r4b.core#4.3.0-snapshot1');
         expect(loadedPackages).toContain('sushi-r5forR4#1.0.0');
-        expect(loadedPackages).toContain('hl7.fhir.uv.tools#current');
+        expect(loadedPackages).toContain('hl7.fhir.uv.tools.r4#9.9.9');
         expect(loadedPackages).toContain('hl7.terminology.r4#9.9.9');
         expect(loggerSpy.getLastMessage('warn')).toMatch(
           /support for pre-release versions of FHIR is experimental/s
@@ -765,7 +765,7 @@ describe('Processing', () => {
         expect(loadedPackages).toHaveLength(1 + NUM_R4_AUTO_DEPENDENCIES);
         expect(loadedPackages).toContain('hl7.fhir.r4b.core#4.3.0');
         expect(loadedPackages).toContain('sushi-r5forR4#1.0.0');
-        expect(loadedPackages).toContain('hl7.fhir.uv.tools#current');
+        expect(loadedPackages).toContain('hl7.fhir.uv.tools.r4#9.9.9');
         expect(loadedPackages).toContain('hl7.terminology.r4#9.9.9');
         expect(loggerSpy.getAllLogs('warn')).toHaveLength(0);
       });
@@ -779,7 +779,7 @@ describe('Processing', () => {
         const loadedPackages = defs.findPackageInfos('*').map(pkg => `${pkg.name}#${pkg.version}`);
         expect(loadedPackages).toHaveLength(1 + NUM_R5_AUTO_DEPENDENCIES);
         expect(loadedPackages).toContain('hl7.fhir.r5.core#4.5.0');
-        expect(loadedPackages).toContain('hl7.fhir.uv.tools#current');
+        expect(loadedPackages).toContain('hl7.fhir.uv.tools.r5#9.9.9');
         expect(loadedPackages).toContain('hl7.terminology.r5#9.9.9');
         expect(loggerSpy.getLastMessage('warn')).toMatch(
           /support for pre-release versions of FHIR is experimental/s
@@ -795,7 +795,7 @@ describe('Processing', () => {
         const loadedPackages = defs.findPackageInfos('*').map(pkg => `${pkg.name}#${pkg.version}`);
         expect(loadedPackages).toHaveLength(1 + NUM_R5_AUTO_DEPENDENCIES);
         expect(loadedPackages).toContain('hl7.fhir.r5.core#5.0.0-snapshot1');
-        expect(loadedPackages).toContain('hl7.fhir.uv.tools#current');
+        expect(loadedPackages).toContain('hl7.fhir.uv.tools.r5#9.9.9');
         expect(loadedPackages).toContain('hl7.terminology.r5#9.9.9');
         expect(loggerSpy.getLastMessage('warn')).toMatch(
           /support for pre-release versions of FHIR is experimental/s
@@ -811,7 +811,7 @@ describe('Processing', () => {
         const loadedPackages = defs.findPackageInfos('*').map(pkg => `${pkg.name}#${pkg.version}`);
         expect(loadedPackages).toHaveLength(1 + NUM_R5_AUTO_DEPENDENCIES);
         expect(loadedPackages).toContain('hl7.fhir.r5.core#5.0.0');
-        expect(loadedPackages).toContain('hl7.fhir.uv.tools#current');
+        expect(loadedPackages).toContain('hl7.fhir.uv.tools.r5#9.9.9');
         expect(loadedPackages).toContain('hl7.terminology.r5#9.9.9');
         expect(loadedPackages).toContain('hl7.fhir.uv.extensions.r5#9.9.9');
         expect(loggerSpy.getAllLogs('warn')).toHaveLength(0);
@@ -826,7 +826,7 @@ describe('Processing', () => {
         const loadedPackages = defs.findPackageInfos('*').map(pkg => `${pkg.name}#${pkg.version}`);
         expect(loadedPackages).toHaveLength(1 + NUM_R5_AUTO_DEPENDENCIES);
         expect(loadedPackages).toContain('hl7.fhir.r5.core#current');
-        expect(loadedPackages).toContain('hl7.fhir.uv.tools#current');
+        expect(loadedPackages).toContain('hl7.fhir.uv.tools.r5#9.9.9');
         expect(loadedPackages).toContain('hl7.terminology.r5#9.9.9');
         expect(loadedPackages).toContain('hl7.fhir.uv.extensions.r5#9.9.9');
         expect(loggerSpy.getLastMessage('warn')).toMatch(
@@ -995,7 +995,7 @@ describe('Processing', () => {
         const loadedPackages = defs.findPackageInfos('*').map(pkg => `${pkg.name}#${pkg.version}`);
         expect(loadedPackages).toHaveLength(4);
         expect(loadedPackages).toContain('sushi-r5forR4#1.0.0');
-        expect(loadedPackages).toContain('hl7.fhir.uv.tools#current');
+        expect(loadedPackages).toContain('hl7.fhir.uv.tools.r4#9.9.9');
         expect(loadedPackages).toContain('hl7.terminology.r4#9.9.9');
         expect(loadedPackages).toContain('hl7.fhir.uv.extensions.r4#9.9.9');
         expect(loggerSpy.getAllMessages('warn')).toHaveLength(0);
@@ -1010,7 +1010,7 @@ describe('Processing', () => {
         const loadedPackages = defs.findPackageInfos('*').map(pkg => `${pkg.name}#${pkg.version}`);
         expect(loadedPackages).toHaveLength(4);
         expect(loadedPackages).toContain('sushi-r5forR4#1.0.0');
-        expect(loadedPackages).toContain('hl7.fhir.uv.tools#current');
+        expect(loadedPackages).toContain('hl7.fhir.uv.tools.r4#9.9.9');
         expect(loadedPackages).toContain('hl7.terminology.r4#9.9.9');
         expect(loadedPackages).toContain('hl7.fhir.uv.extensions.r4#9.9.9');
         expect(loggerSpy.getAllMessages('warn')).toHaveLength(0);
@@ -1024,7 +1024,7 @@ describe('Processing', () => {
       return loadAutomaticDependencies('5.0.0-draft-final', config.dependencies, defs).then(() => {
         const loadedPackages = defs.findPackageInfos('*').map(pkg => `${pkg.name}#${pkg.version}`);
         expect(loadedPackages).toHaveLength(3);
-        expect(loadedPackages).toContain('hl7.fhir.uv.tools#current');
+        expect(loadedPackages).toContain('hl7.fhir.uv.tools.r5#9.9.9');
         expect(loadedPackages).toContain('hl7.terminology.r5#9.9.9');
         expect(loadedPackages).toContain('hl7.fhir.uv.extensions.r5#9.9.9');
         expect(loggerSpy.getAllMessages('warn')).toHaveLength(0);
@@ -1038,7 +1038,7 @@ describe('Processing', () => {
       return loadAutomaticDependencies('5.0.0', config.dependencies, defs).then(() => {
         const loadedPackages = defs.findPackageInfos('*').map(pkg => `${pkg.name}#${pkg.version}`);
         expect(loadedPackages).toHaveLength(3);
-        expect(loadedPackages).toContain('hl7.fhir.uv.tools#current');
+        expect(loadedPackages).toContain('hl7.fhir.uv.tools.r5#9.9.9');
         expect(loadedPackages).toContain('hl7.terminology.r5#9.9.9');
         expect(loadedPackages).toContain('hl7.fhir.uv.extensions.r5#9.9.9');
         expect(loggerSpy.getAllMessages('warn')).toHaveLength(0);
@@ -1052,7 +1052,7 @@ describe('Processing', () => {
       return loadAutomaticDependencies('6.0.0-ballot2', config.dependencies, defs).then(() => {
         const loadedPackages = defs.findPackageInfos('*').map(pkg => `${pkg.name}#${pkg.version}`);
         expect(loadedPackages).toHaveLength(3);
-        expect(loadedPackages).toContain('hl7.fhir.uv.tools#current');
+        expect(loadedPackages).toContain('hl7.fhir.uv.tools.r5#9.9.9');
         expect(loadedPackages).toContain('hl7.terminology.r5#9.9.9');
         expect(loadedPackages).toContain('hl7.fhir.uv.extensions.r5#9.9.9');
         expect(loggerSpy.getAllMessages('warn')).toHaveLength(0);
@@ -1061,7 +1061,7 @@ describe('Processing', () => {
 
     it('should not load dependencies that are present in the config', async () => {
       const config = cloneDeep(minimalConfig);
-      config.dependencies = [{ packageId: 'hl7.fhir.uv.tools', version: '2.2.0-test' }];
+      config.dependencies = [{ packageId: 'hl7.fhir.uv.tools.r4', version: '2.2.0-test' }];
       const defs = await getTestFHIRDefinitions();
       return loadAutomaticDependencies(config.fhirVersion[0], config.dependencies, defs).then(
         () => {
@@ -1069,23 +1069,7 @@ describe('Processing', () => {
             .findPackageInfos('*')
             .map(pkg => `${pkg.name}#${pkg.version}`);
           expect(loadedPackages).toHaveLength(NUM_R4_AUTO_DEPENDENCIES - 1);
-          expect(loadedPackages).not.toContain('hl7.fhir.uv.tools#current');
-          expect(loggerSpy.getAllMessages('warn')).toHaveLength(0);
-        }
-      );
-    });
-
-    it('should not load dependencies that are present in the config even if they have an r{x} suffix and the auto dependency does not', async () => {
-      const config = cloneDeep(minimalConfig);
-      config.dependencies = [{ packageId: 'hl7.fhir.uv.tools.r4', version: '4.0.0-test' }];
-      const defs = await getTestFHIRDefinitions();
-      return loadAutomaticDependencies(config.fhirVersion[0], config.dependencies, defs).then(
-        () => {
-          const loadedPackages = defs
-            .findPackageInfos('*')
-            .map(pkg => `${pkg.name}#${pkg.version}`);
-          expect(loadedPackages).toHaveLength(NUM_R4_AUTO_DEPENDENCIES - 1);
-          expect(loadedPackages).not.toContain('hl7.fhir.uv.tools#current');
+          expect(loadedPackages).not.toContain('hl7.fhir.uv.tools.r4#9.9.9');
           expect(loggerSpy.getAllMessages('warn')).toHaveLength(0);
         }
       );
@@ -1129,9 +1113,9 @@ describe('Processing', () => {
       const config = cloneDeep(minimalConfig);
       config.dependencies = [{ packageId: 'hl7.fhir.us.core', version: '3.1.0' }];
       const defs = await getTestFHIRDefinitions();
-      defs.currentBuildClientMock.downloadCurrentBuild
+      defs.registryClientMock.download
         .mockReset()
-        .calledWith('hl7.fhir.uv.tools')
+        .calledWith('hl7.fhir.uv.tools.r4', '9.9.9')
         .mockRejectedValue(new Error());
       return loadAutomaticDependencies(config.fhirVersion[0], config.dependencies, defs).then(
         () => {
@@ -1139,10 +1123,10 @@ describe('Processing', () => {
             .findPackageInfos('*')
             .map(pkg => `${pkg.name}#${pkg.version}`);
           expect(loadedPackages).toHaveLength(NUM_R4_AUTO_DEPENDENCIES - 1);
-          expect(loadedPackages).not.toContain('hl7.fhir.uv.tools#current');
+          expect(loadedPackages).not.toContain('hl7.fhir.uv.tools.r4#9.9.9');
           expect(loggerSpy.getAllMessages('warn')).toHaveLength(1);
           expect(loggerSpy.getLastMessage('warn')).toMatch(
-            /Failed to load automatically-provided hl7\.fhir\.uv\.tools#current/s
+            /Failed to load automatically-provided hl7\.fhir\.uv\.tools\.r4#latest/s
           );
           // But don't log the warning w/ details about proxies
           expect(loggerSpy.getLastMessage('warn')).not.toMatch(/SSL/s);