From 04f20f203571584046344d95e46b3c957efaf410 Mon Sep 17 00:00:00 2001
From: Evan Carroll <me@evancarroll.com>
Date: Thu, 29 Jul 2021 12:58:22 -0500
Subject: [PATCH] fix: file labels in GitLab releases, to ensure they're
 unique.

Previously GitLab lables were just the basename for files uploaded as
part of the release. This is problematic because GitLab doesn't allow
conflicting labels -- a condition that could be caused by uploading a
release with two files by the same name in different directories. This
would generate a 409 Conflict error.

This changes the labels for files uploaded as part of a release to the
name relative to pkgRoot, or the package.

A project may look like this

  pkg
  pkg \ foo \ baz
  pkg \ bar \ baz

This would previously result in two conflicting labels of 'baz'. Now you
would have {"foo/baz", "bar/baz"} with no conflict.

GitHub issues: #265, #158
---
 lib/glob-assets.js          |  8 ++++----
 lib/publish.js              |  2 +-
 lib/resolve-config.js       | 33 ++++++++++++++++-----------------
 test/resolve-config.test.js | 11 +++++++++++
 4 files changed, 32 insertions(+), 22 deletions(-)

diff --git a/lib/glob-assets.js b/lib/glob-assets.js
index e8914489..a4e880b1 100644
--- a/lib/glob-assets.js
+++ b/lib/glob-assets.js
@@ -1,12 +1,11 @@
 const path = require('path');
-const {basename} = require('path');
 const {isPlainObject, castArray, uniqWith, uniq} = require('lodash');
 const dirGlob = require('dir-glob');
 const globby = require('globby');
 const debug = require('debug')('semantic-release:gitlab');
 
-module.exports = async ({cwd}, assets) =>
-  uniqWith(
+module.exports = async ({pkgRoot, cwd}, assets) => {
+  return uniqWith(
     []
       .concat(
         ...(await Promise.all(
@@ -42,7 +41,7 @@ module.exports = async ({cwd}, assets) =>
                 // - `filepath` ignored (also to avoid duplicates)
                 // - other properties of the original asset definition
                 const {filepath, ...others} = asset;
-                return globbed.map(file => ({...others, path: file, label: basename(file)}));
+                return globbed.map(file => ({...others, path: file, label: path.relative(pkgRoot || '.', file)}));
               }
 
               // If asset is an Object, output an Object definition with:
@@ -66,3 +65,4 @@ module.exports = async ({cwd}, assets) =>
     // Compare `path` property if Object definition, value itself if String
     (a, b) => path.resolve(cwd, isPlainObject(a) ? a.path : a) === path.resolve(cwd, isPlainObject(b) ? b.path : b)
   );
+};
diff --git a/lib/publish.js b/lib/publish.js
index d2384766..4e25f1a4 100644
--- a/lib/publish.js
+++ b/lib/publish.js
@@ -30,7 +30,7 @@ module.exports = async (pluginConfig, context) => {
   debug('milestones: %o', milestones);
 
   if (assets && assets.length > 0) {
-    const globbedAssets = await getAssets(context, assets);
+    const globbedAssets = await getAssets({cwd: context.cwd, pkgRoot: pluginConfig.pkgRoot}, assets);
     debug('globbed assets: %o', globbedAssets);
 
     await Promise.all(
diff --git a/lib/resolve-config.js b/lib/resolve-config.js
index 8fc3a413..94776393 100644
--- a/lib/resolve-config.js
+++ b/lib/resolve-config.js
@@ -1,23 +1,21 @@
 const {castArray, isNil} = require('lodash');
 const urlJoin = require('url-join');
 
-module.exports = (
-  {gitlabUrl, gitlabApiPathPrefix, assets, milestones},
-  {
-    envCi: {service} = {},
-    env: {
-      CI_PROJECT_URL,
-      CI_PROJECT_PATH,
-      CI_API_V4_URL,
-      GL_TOKEN,
-      GITLAB_TOKEN,
-      GL_URL,
-      GITLAB_URL,
-      GL_PREFIX,
-      GITLAB_PREFIX,
-    },
-  }
-) => {
+module.exports = (pluginConfig, context) => {
+  const {gitlabUrl, gitlabApiPathPrefix, assets, milestones, pkgRoot} = pluginConfig;
+  const {service} = context.envCi || {service: undefined};
+  const {
+    CI_PROJECT_URL,
+    CI_PROJECT_PATH,
+    CI_API_V4_URL,
+    GL_TOKEN,
+    GITLAB_TOKEN,
+    GL_URL,
+    GITLAB_URL,
+    GL_PREFIX,
+    GITLAB_PREFIX,
+  } = context.env;
+
   const userGitlabApiPathPrefix = isNil(gitlabApiPathPrefix)
     ? isNil(GL_PREFIX)
       ? GITLAB_PREFIX
@@ -31,6 +29,7 @@ module.exports = (
       : 'https://gitlab.com');
 
   return {
+    pkgRoot,
     gitlabToken: GL_TOKEN || GITLAB_TOKEN,
     gitlabUrl: defaultedGitlabUrl,
     gitlabApiUrl:
diff --git a/test/resolve-config.test.js b/test/resolve-config.test.js
index 3d0ddfd5..0d1abcf3 100644
--- a/test/resolve-config.test.js
+++ b/test/resolve-config.test.js
@@ -14,6 +14,7 @@ test('Returns user config', t => {
     gitlabApiUrl: urlJoin(gitlabUrl, gitlabApiPathPrefix),
     assets,
     milestones: undefined,
+    pkgRoot: undefined,
   });
 });
 
@@ -35,6 +36,7 @@ test('Returns user config via environment variables', t => {
       gitlabApiUrl: urlJoin(gitlabUrl, gitlabApiPathPrefix),
       assets,
       milestones,
+      pkgRoot: undefined,
     }
   );
 });
@@ -53,6 +55,7 @@ test('Returns user config via alternative environment variables', t => {
       gitlabApiUrl: urlJoin(gitlabUrl, gitlabApiPathPrefix),
       assets,
       milestones: undefined,
+      pkgRoot: undefined,
     }
   );
 });
@@ -68,6 +71,7 @@ test('Returns default config', t => {
     gitlabApiUrl: urlJoin('https://gitlab.com', '/api/v4'),
     assets: undefined,
     milestones: undefined,
+    pkgRoot: undefined,
   });
 
   t.deepEqual(resolveConfig({gitlabApiPathPrefix}, {env: {GL_TOKEN: gitlabToken}}), {
@@ -76,6 +80,7 @@ test('Returns default config', t => {
     gitlabApiUrl: urlJoin('https://gitlab.com', gitlabApiPathPrefix),
     assets: undefined,
     milestones: undefined,
+    pkgRoot: undefined,
   });
 
   t.deepEqual(resolveConfig({gitlabUrl}, {env: {GL_TOKEN: gitlabToken}}), {
@@ -84,6 +89,7 @@ test('Returns default config', t => {
     gitlabApiUrl: urlJoin(gitlabUrl, '/api/v4'),
     assets: undefined,
     milestones: undefined,
+    pkgRoot: undefined,
   });
 });
 
@@ -107,6 +113,7 @@ test('Returns default config via GitLab CI/CD environment variables', t => {
       gitlabApiUrl: CI_API_V4_URL,
       assets: undefined,
       milestones: undefined,
+      pkgRoot: undefined,
     }
   );
 });
@@ -134,6 +141,7 @@ test('Returns user config over GitLab CI/CD environment variables', t => {
       gitlabApiUrl: urlJoin(gitlabUrl, gitlabApiPathPrefix),
       assets,
       milestones: undefined,
+      pkgRoot: undefined,
     }
   );
 });
@@ -167,6 +175,7 @@ test('Returns user config via environment variables over GitLab CI/CD environmen
       gitlabApiUrl: urlJoin(gitlabUrl, gitlabApiPathPrefix),
       assets: undefined,
       milestones: undefined,
+      pkgRoot: undefined,
     }
   );
 });
@@ -200,6 +209,7 @@ test('Returns user config via alternative environment variables over GitLab CI/C
       gitlabApiUrl: urlJoin(gitlabUrl, gitlabApiPathPrefix),
       assets: undefined,
       milestones: undefined,
+      pkgRoot: undefined,
     }
   );
 });
@@ -224,6 +234,7 @@ test('Ignore GitLab CI/CD environment variables if not running on GitLab CI/CD',
       gitlabApiUrl: urlJoin('https://gitlab.com', '/api/v4'),
       assets: undefined,
       milestones: undefined,
+      pkgRoot: undefined,
     }
   );
 });