diff mbox series

[v3,1/1] fetch2/npm: Adding npmrc support for private registry authentication

Message ID 20250226124720.6462-2-eric.meyers@arthrex.com
State Accepted, archived
Commit 5fa6137b6d98544766f3152b874e67d04fafb88f
Headers show
Series NPM Fetcher Private Registry Authentication Support: | expand

Commit Message

Eric Meyers Feb. 26, 2025, 12:47 p.m. UTC
Signed-off-by: Eric Meyers <eric.meyers@arthrex.com>
Cc: Geoff Parker <geoffrey.parker@arthrex.com>
Cc: Alexander Kanavin <alex.kanavin@gmail.com>
Cc: Chuck Wolber <chuckwolber@gmail.com>
---
 lib/bb/fetch2/npm.py | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

Comments

Chuck Wolber Feb. 26, 2025, 7:14 p.m. UTC | #1
On Wed, Feb 26, 2025 at 4:47 AM Eric Meyers <eric.meyers15310@gmail.com>
wrote:

> Signed-off-by: Eric Meyers <eric.meyers@arthrex.com>
> Cc: Geoff Parker <geoffrey.parker@arthrex.com>
> Cc: Alexander Kanavin <alex.kanavin@gmail.com>
> Cc: Chuck Wolber <chuckwolber@gmail.com>
> ---
>  lib/bb/fetch2/npm.py | 16 ++++++++++++++++
>  1 file changed, 16 insertions(+)
>
> diff --git a/lib/bb/fetch2/npm.py b/lib/bb/fetch2/npm.py
> index c09f05044..7486049b8 100644
> --- a/lib/bb/fetch2/npm.py
> +++ b/lib/bb/fetch2/npm.py
> @@ -107,6 +107,22 @@ class NpmEnvironment(object):
>          """Run npm command in a controlled environment"""
>          with tempfile.TemporaryDirectory() as tmpdir:
>              d = bb.data.createCopy(self.d)
> +
> +            # If the user opts to use their own NPMRC file, then use it
> within the NPM environment
> +            # (along with the user provided configs). Otherwise, leave it
> blank.
> +            if d.getVar("BB_USE_HOME_NPMRC_FILE") == "1":
> +                home_npmrc_file = os.path.join(os.environ.get("HOME"),
> ".npmrc")
> +                tmp_npmrc_file = os.path.join(tmpdir, ".npmrc")
> +                bb.warn("BB_USE_HOME_NPMRC_FILE flag is set - NPM fetcher
> will now use the .npmrc file in the HOME directory.")
> +                if os.path.exists(home_npmrc_file):
> +                    bb.utils.copyfile(home_npmrc_file, tmp_npmrc_file)
> +                    # Still need to append the contents of the user
> config created in __init__
> +                    with open(tmp_npmrc_file, 'a') as t:
> +                        with open(self.user_config.name, 'r') as u:
> +                            t.write(u.read())
> +                            t.flush()
> +                    self.user_config.name = tmp_npmrc_file
> +
>              d.setVar("PATH", d.getVar("PATH"))  # PATH might contain
> $HOME - evaluate it before patching
>              d.setVar("HOME", tmpdir)
>
>

Thank you for the patch! I see a few nits.

Recommendations further down in this message somewhat obviate this first
one, but I am including it anyway for completeness. I think you should
combine the two if statements. There does not seem to be any point in
printing the bb.warn if there is no ${HOME}/.nmprc file.

I like the use of the BB_USE_ prefix, because it vests the policy decision
to
allow outside influence with bitbake, where it belongs. However I recommend
shortening it a bit to BB_USE_HOME_NPMRC. The addition of _FILE seems a
little
redundant, and it narrows the future options a bit more than necessary.

And, given that you are adding a new variable, I think you should include a
patch to add that variable to the bitbake manual.

Rather than clutter things up in the run() method, you can simplify your
change greatly by creating a separate instance method (e.g.
__home_npmrc(..))
in the NpmEnvironment class which only returns a file path, or None. It is
in
that method that you would check for the ${HOME}/.npmrc file, the
BB_HOME_NPMRC
flag, and do the bb.warn. Then you would return the string that is the file
path to the user's .npmrc. No need for any file copy, or renaming.

Then in NpmEnvironment __init__ you would do something like this:

self.user_config = self.home_npmrc(self, d)
if self.user_config is None:
    self.user_config = tempfile.NamedTemporaryFile(mode="w", buffering=1)
for key, value in configs:
    self.user_config.write("%s=%s\n" % (key, value))

That way you get everything you were looking for without an extra file copy
and name swap.

**IMPORTANT** There is an obvious bug in my example code that I do not have
time to resolve. You need to open the self.user_config file for appending
if it
is not None. You should do all this in a with block so you do not have to
worry
about closing file handles.

Also worth noting, the npm.bbclass in oe-core uses the configs variable in
__init__ for global config, which is then interpreted as user_config. I do
not think that is necessarily going to present a problem, but it might be a
refactor target down the line (or now?) to make the distinction between user
and global config more clear.

..Ch:W..
Chuck Wolber Feb. 27, 2025, 4:48 a.m. UTC | #2
On Wed, Feb 26, 2025 at 11:14 AM Chuck Wolber <chuckwolber@gmail.com> wrote:

>
> On Wed, Feb 26, 2025 at 4:47 AM Eric Meyers <eric.meyers15310@gmail.com>
> wrote:
>
>> Signed-off-by: Eric Meyers <eric.meyers@arthrex.com>
>> Cc: Geoff Parker <geoffrey.parker@arthrex.com>
>> Cc: Alexander Kanavin <alex.kanavin@gmail.com>
>> Cc: Chuck Wolber <chuckwolber@gmail.com>
>> ---
>>  lib/bb/fetch2/npm.py | 16 ++++++++++++++++
>>  1 file changed, 16 insertions(+)
>>
>> diff --git a/lib/bb/fetch2/npm.py b/lib/bb/fetch2/npm.py
>> index c09f05044..7486049b8 100644
>> --- a/lib/bb/fetch2/npm.py
>> +++ b/lib/bb/fetch2/npm.py
>> @@ -107,6 +107,22 @@ class NpmEnvironment(object):
>>          """Run npm command in a controlled environment"""
>>          with tempfile.TemporaryDirectory() as tmpdir:
>>              d = bb.data.createCopy(self.d)
>> +
>> +            # If the user opts to use their own NPMRC file, then use it
>> within the NPM environment
>> +            # (along with the user provided configs). Otherwise, leave
>> it blank.
>> +            if d.getVar("BB_USE_HOME_NPMRC_FILE") == "1":
>> +                home_npmrc_file = os.path.join(os.environ.get("HOME"),
>> ".npmrc")
>> +                tmp_npmrc_file = os.path.join(tmpdir, ".npmrc")
>> +                bb.warn("BB_USE_HOME_NPMRC_FILE flag is set - NPM
>> fetcher will now use the .npmrc file in the HOME directory.")
>> +                if os.path.exists(home_npmrc_file):
>> +                    bb.utils.copyfile(home_npmrc_file, tmp_npmrc_file)
>> +                    # Still need to append the contents of the user
>> config created in __init__
>> +                    with open(tmp_npmrc_file, 'a') as t:
>> +                        with open(self.user_config.name, 'r') as u:
>> +                            t.write(u.read())
>> +                            t.flush()
>> +                    self.user_config.name = tmp_npmrc_file
>> +
>>              d.setVar("PATH", d.getVar("PATH"))  # PATH might contain
>> $HOME - evaluate it before patching
>>              d.setVar("HOME", tmpdir)
>>
>>
>
> Thank you for the patch! I see a few nits.
>
> Recommendations further down in this message somewhat obviate this first
> one, but I am including it anyway for completeness. I think you should
> combine the two if statements. There does not seem to be any point in
> printing the bb.warn if there is no ${HOME}/.nmprc file.
>
> I like the use of the BB_USE_ prefix, because it vests the policy decision
> to
> allow outside influence with bitbake, where it belongs. However I recommend
> shortening it a bit to BB_USE_HOME_NPMRC. The addition of _FILE seems a
> little
> redundant, and it narrows the future options a bit more than necessary.
>
> And, given that you are adding a new variable, I think you should include a
> patch to add that variable to the bitbake manual.
>
> Rather than clutter things up in the run() method, you can simplify your
> change greatly by creating a separate instance method (e.g.
> __home_npmrc(..))
> in the NpmEnvironment class which only returns a file path, or None. It is
> in
> that method that you would check for the ${HOME}/.npmrc file, the
> BB_HOME_NPMRC
> flag, and do the bb.warn. Then you would return the string that is the file
> path to the user's .npmrc. No need for any file copy, or renaming.
>
> Then in NpmEnvironment __init__ you would do something like this:
>
> self.user_config = self.home_npmrc(self, d)
> if self.user_config is None:
>     self.user_config = tempfile.NamedTemporaryFile(mode="w", buffering=1)
> for key, value in configs:
>     self.user_config.write("%s=%s\n" % (key, value))
>


Apologies for not proof-reading my code. That example code is obviously not
what you would want to do. Hopefully the actual meaning bled through
somehow.

I meant something closer to this:

self.user_config = tempfile.NamedTemporaryFile(mode="w", buffering=1)
hn = self.home_npmrc(self, d)
if hn is not None:
   with open(hn, 'r') as hnf:
      self.user_config.write(hnf.read())
for key, value in configs:
   self.user_config.write("%s=%s\n" % (key, value))


..Ch:W..
Eric Meyers Feb. 27, 2025, 5:20 p.m. UTC | #3
On Wed, Feb 26, 2025 at 10:49 PM Chuck Wolber <chuckwolber@gmail.com> wrote:

>
> On Wed, Feb 26, 2025 at 11:14 AM Chuck Wolber <chuckwolber@gmail.com>
> wrote:
>
>>
>> Thank you for the patch! I see a few nits.
>>
>> Recommendations further down in this message somewhat obviate this first
>> one, but I am including it anyway for completeness. I think you should
>> combine the two if statements. There does not seem to be any point in
>> printing the bb.warn if there is no ${HOME}/.nmprc file.
>>
>> I like the use of the BB_USE_ prefix, because it vests the policy
>> decision to
>> allow outside influence with bitbake, where it belongs. However I
>> recommend
>> shortening it a bit to BB_USE_HOME_NPMRC. The addition of _FILE seems a
>> little
>> redundant, and it narrows the future options a bit more than necessary.
>>
>> And, given that you are adding a new variable, I think you should include
>> a
>> patch to add that variable to the bitbake manual.
>>
>> Rather than clutter things up in the run() method, you can simplify your
>> change greatly by creating a separate instance method (e.g.
>> __home_npmrc(..))
>> in the NpmEnvironment class which only returns a file path, or None. It
>> is in
>> that method that you would check for the ${HOME}/.npmrc file, the
>> BB_HOME_NPMRC
>> flag, and do the bb.warn. Then you would return the string that is the
>> file
>> path to the user's .npmrc. No need for any file copy, or renaming.
>>
>> Then in NpmEnvironment __init__ you would do something like this:
>>
>> self.user_config = self.home_npmrc(self, d)
>> if self.user_config is None:
>>     self.user_config = tempfile.NamedTemporaryFile(mode="w", buffering=1)
>> for key, value in configs:
>>     self.user_config.write("%s=%s\n" % (key, value))
>>
>
>
> Apologies for not proof-reading my code. That example code is obviously not
> what you would want to do. Hopefully the actual meaning bled through
> somehow.
>
> I meant something closer to this:
>
> self.user_config = tempfile.NamedTemporaryFile(mode="w", buffering=1)
> hn = self.home_npmrc(self, d)
> if hn is not None:
>    with open(hn, 'r') as hnf:
>       self.user_config.write(hnf.read())
> for key, value in configs:
>    self.user_config.write("%s=%s\n" % (key, value))
>
>
> ..Ch:W..
>

Chuck,

Thanks a ton for the feedback - much appreciated. I will take some time to
digest this, do the rework, and eventually submit a v4 patch. Probably
sometime early next week.

Thanks!,
-Eric
diff mbox series

Patch

diff --git a/lib/bb/fetch2/npm.py b/lib/bb/fetch2/npm.py
index c09f05044..7486049b8 100644
--- a/lib/bb/fetch2/npm.py
+++ b/lib/bb/fetch2/npm.py
@@ -107,6 +107,22 @@  class NpmEnvironment(object):
         """Run npm command in a controlled environment"""
         with tempfile.TemporaryDirectory() as tmpdir:
             d = bb.data.createCopy(self.d)
+
+            # If the user opts to use their own NPMRC file, then use it within the NPM environment
+            # (along with the user provided configs). Otherwise, leave it blank.
+            if d.getVar("BB_USE_HOME_NPMRC_FILE") == "1":
+                home_npmrc_file = os.path.join(os.environ.get("HOME"), ".npmrc")
+                tmp_npmrc_file = os.path.join(tmpdir, ".npmrc")
+                bb.warn("BB_USE_HOME_NPMRC_FILE flag is set - NPM fetcher will now use the .npmrc file in the HOME directory.")
+                if os.path.exists(home_npmrc_file):
+                    bb.utils.copyfile(home_npmrc_file, tmp_npmrc_file)
+                    # Still need to append the contents of the user config created in __init__
+                    with open(tmp_npmrc_file, 'a') as t:
+                        with open(self.user_config.name, 'r') as u:
+                            t.write(u.read())
+                            t.flush()
+                    self.user_config.name = tmp_npmrc_file
+
             d.setVar("PATH", d.getVar("PATH"))  # PATH might contain $HOME - evaluate it before patching
             d.setVar("HOME", tmpdir)