Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: rewrite to pure js #82

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open

feat: rewrite to pure js #82

wants to merge 20 commits into from

Conversation

bxb100
Copy link

@bxb100 bxb100 commented Nov 20, 2024

as mentioned in #80, this is a PR for rewriting to js

PS. I can't pass the default lint config, so I choose to use the GitHub action template eslint.yml

I tested Service Accounts and 1Password Connect locally using ⁠npm run local-action. If any reviewer is interested, you can test it locally by copying ⁠.env.example to ⁠.env.

Copy link

@Xuanwo Xuanwo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @bxb100, for creating this; it's really great!

One suggestion I have is to add a CI check to verify that dist/core_bg.wasm and dist/index.js haven't been changed. I understand they are generated by js tools, and I trust all of you (@bxb100 and 1Password team members). However, it always makes me a bit concerned that someone might change the dist files by mistake. By adding CI checks, we ensure that the dist files are exactly what we expect to see and haven't been altered.

src/service/account.ts Outdated Show resolved Hide resolved
@Xuanwo
Copy link

Xuanwo commented Nov 20, 2024

NOTE to other reviewers: This PR is large due to the generated dist files; however, the significant changes are quite minimal.

@bxb100
Copy link
Author

bxb100 commented Nov 21, 2024

@Xuanwo Thank you for the review. I have learned a lot from your comments about the CI check. I have added a new workflow, ⁠check-dist.yml, to ensure the generated dist is reliable.

@Xuanwo
Copy link

Xuanwo commented Nov 21, 2024

@Xuanwo Thank you for the review. I have learned a lot from your comments about the CI check. I have added a new workflow, ⁠check-dist.yml, to ensure the generated dist is reliable.

Thank you, mostly LGTM now!

@Xuanwo
Copy link

Xuanwo commented Nov 21, 2024

Inviting @edif2008 and @SimonBarendse for a review.

@SimonBarendse
Copy link
Member

SimonBarendse commented Nov 22, 2024

Hi folks, really appreciate your efforts on this! ❤️ 🙌 Definitely aligned with the direction we'd like to move in for this and other integrations. We've build SDKs exactly because we saw such large desire from all of you to build integrations, and friction from doing so with CLI (e.g. performance, support for self-hosted runners, Windows support and it removes complexity from the implementation, making it easier to maintain and less error-prone). Excited to see you already help us move in that direction! 🚀

A couple things we'll want to look at to get this merged and deployed:

  • Backwards compatibility: Continuity is quite important to us. We realize many depend on this and other integrations in critical workflows, and want to avoid disruptions as much as possible. With that in mind, we'll want to have a look at whether we can find a path to keep this action backwards compatible and at v2. So far, desktop app integration is the only missing feature in SDK we've identified to make a fully backwards compatible migration possible. While we work on closing that gap in SDKs, to not block this work from moving forward and make those aforementioned improvements available when not running locally (probably most uses), shall we support both side-by-side for now? We can check for Service Account token and/or Connect token envvars to use the new SDKs, and fall back to requiring CLI when those are not present (and thus probably running local). Once SDKs support Auth Prompts (aka desktop app integration) we can fully move over to SDK-only solution. That'd be a fully backwards compatible migration and we could stick to v2 throughout.
  • Dist: Not sure why this action is using dist. @edif2008 is currently out, but when he returns next week, would love to learn from him what our reasoning was for introducing and if we could move away from that now. This could remove the complexity of required checks and prevent this repo from bloating by checking in a new binary for the WASM build on every new SDK release. Binary is already 5MB right now, and will likely grow further when expanding SDKs with additional functionality. With frequent SDK releases can add up quite quickly.
  • Atomic Commits / PRs: To make the changes easier to review and move through faster, could you separate out unrelated changes in separate commits and PRs? For example I believe the changes to CONTRIBUTING.md, integration rename, linting changes, .gitignore additions, TS Config changes and .env.example introduction could each go in separate PRs, unless there's a relation that I'm not understanding yet. In that case, could you explain these changes and how they relate to this rewrite to help us understand and review? Shout-out to @AleksandrHovhannisyan for writing a great blog on this topic: https://www.aleksandrhovhannisyan.com/blog/atomic-git-commits/ 🙌

@Xuanwo
Copy link

Xuanwo commented Nov 22, 2024

Thank you @SimonBarendse for the bravo comments!

@bxb100 led the entire effort, so I will leave the final decision to him on how to implement changes. I'm here to explain why we need dist/.

For every uses: actions/checkout@v4, the GitHub Actions runner will clone the specified action actions/checkout and directly execute its entrypoint. There is no build or compile setup involved, so each action must include a bundled index.js file for the action to function properly.

The entry point is defined here:

runs:
using: "node20"
main: "dist/index.js"

Additional documentation can be found here: https://docs.github.com/en/actions/sharing-automations/creating-actions/creating-a-javascript-action

@yafanasiev
Copy link

I am not sure if there are some dependencies in 1Password SDK that require ncc and wasm specifically, but I've had a great experience bundling JS GitHub Actions with esbuild. It's fast, supports tree shaking and the final bundle is smaller than NCC one.

@Xuanwo
Copy link

Xuanwo commented Nov 22, 2024

I am not sure if there are some dependencies in 1Password SDK that require ncc and wasm specifically

I'm guessing 1Password has written their logic in Rust, with the JS SDK being just a wrapper.

@SimonBarendse
Copy link
Member

I'm guessing 1Password has written their logic in Rust, with the JS SDK being just a wrapper.

Yes, this is exactly it. We're using WASM to share a single implementation (in Rust) between all languages we support SDKs in. Right now that's Go, Python and JS, and we've built it with the intention to grow to more languages (in a scalable & sustainable way).

@SimonBarendse
Copy link
Member

Thanks for the explanation on why dist is required @Xuanwo ! ❤️ That makes sense.

@bxb100
Copy link
Author

bxb100 commented Nov 23, 2024

update 11/23: force update with minimal changes. the original version lives in branch #82-bak


Thank you @Xuanwo, @SimonBarendse, and @yafanasiev for your reviews and discussions. I would like to provide some explanations and background information regarding the submitted content in this PR:

  • .env.example is an example config used for local-action, which facilitates local testing directly.
  • .eslintignore, .github/linters/.eslintrc.yml, package.json (deleting eslintConfig) are changes made because I couldn't use the original @1password/front-end-style/eslintrc.yml when submitting the code. Therefore, I switched to the configuration recommended by https://github.com/actions/typescript-action.
  • __tests__, config/jest.config.js are to expand the original test set.
  • .github/workflows/check-dist.yml, .node-version are used to check the consistency of the generated dist for every PR and PUSH as mentioned before. It requires executing npm run bundle, which includes format:write.
  • CONTRIBUTING.md results from the pre-push hook from husky executing npm run validate(contain format). I apologize for its appearance in this submission.
  • The changes to .github/linters/tsconfig.json and tsconfig.json will be reverted.
  • Regarding the fallback to CLI when there are no environment variables, perhaps we can follow @Xuanwo 's suggestion and bump into v3, then iterate gradually. I understand this occurs mainly during local testing, but it can also be bypassed by setting the environment variables.
  • About compatibility, the inputs in action.yml remain consistent with version v2. The environment variables OP_SERVICE_ACCOUNT_TOKEN, OP_CONNECT_HOST, and OP_CONNECT_TOKEN also remain unchanged.
  • Based on my understanding of action uses 1, I believe frequently changing the dist output will only affect users who do not use the major version and need to update manually (although, to be honest, I haven't seen this usage scenario).
  • For the bundled submission of this PR, since it was converted from one of my projects 2, I did not consider atomic submissions. I sincerely apologize for any inconvenience this may have caused.

Footnotes

  1. https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses

  2. https://github.com/bxb100/load-secrets-action

@Xuanwo
Copy link

Xuanwo commented Nov 23, 2024

Backwards compatibility

Hi @SimonBarendse, I suggest we avoid taking this direction since switching from CLI-based to JS-based does introduce a breaking change. I can foresee multiple ways this action could fail. It's common and expected for GitHub Actions users to upgrade actions between major versions (every Node version bump will trigger one).

Adopting load-secrets-action@v3 would provide a much smoother transition for the community. Releasing v3 doesn't mean users will adopt it by default; they will continue using v2 unless they manually update their workflows.

I am willing to create a migration guide for users to follow if there are any break happened.

  • For the bundled submission of this PR, since it was converted from one of my projects 2, I did not consider atomic submissions. I sincerely apologize for any inconvenience this may have caused.

Hi @bxb100, I understand the concern from @SimonBarendse's side. To make the PR review smoother, do you think it would be a good idea to simply remove all files except for package.json, src/, and dist/? We can gradually add other staffs back later (which makes it much easier to review without involving real logic).

@SimonBarendse
Copy link
Member

SimonBarendse commented Nov 25, 2024

I would like to provide some explanations and background information regarding the submitted content in this PR

Thank you @bxb100! 🙌 This is very helpful.

Followed up in separate issues

Rewrite specific changes - Discussing in this PR

Backwards Compatibility

I suggest we avoid taking this direction since switching from CLI-based to JS-based does introduce a breaking change. I can foresee multiple ways this action could fail.

@Xuanwo could you help me see these too? I'm currently under the impression that the new version of this action built on SDK will be fully compatible with the current version built on CLI, after addition of Auth Prompts. As @bxb100 mentioned as well:

the inputs in action.yml remain consistent with version v2. The environment variables OP_SERVICE_ACCOUNT_TOKEN, OP_CONNECT_HOST, and OP_CONNECT_TOKEN also remain unchanged.

Could you please help me see and understand what I'm missing? What are the failures you foresee?

Dist

Yes, I'm less concerned about this now and okay with proceeding with checking in the WASM binary into the code here after @Xuanwo's explanation why this is necessary. Apologies that I not explicitly mentioned that before.

I assume and hope when using the action that GitHub only fetches the referenced commit and not the full Git history, similar to the checkout action, by using --depth=1. That prevents historic binaries slowing down the action and increasing required bandwidth.

So the larger repo size from historic binaries checked in would only affect new contributors checking out the full repo, including all historic WASM binaries. But it won't affect using the action.

With that in mind, I believe we can pull back in the changes to dist and your introduction of .github/workflows/check-dist.yml.

Code Review

I've requested a review from @Marton6 and @edif2008 for the specific code changes made in this PR.

@bxb100
Copy link
Author

bxb100 commented Nov 27, 2024

Thank you, @SimonBarendse, for the detailed explanation.

With that in mind, I believe we can pull back in the changes to dist and your introduction of .github/workflows/check-dist.yml.

I believe we can proceed after #83 #84 .

I assume and hope when using the action that GitHub only fetches the referenced commit and not the full Git history, similar to the checkout action, by using --depth=1. That prevents historic binaries slowing down the action and increasing required bandwidth.

base on https://github.com/actions/runner/blob/078eb3b381939ee6665f545234e1dca5ed07da84/src/Runner.Worker/ActionManager.cs#L784C1-L792C1 I think it direct download like https://api.github.com/repos/{action.NameWithOwner}/tarball/{action.Ref}

Copy link
Member

@edif2008 edif2008 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @bxb100!

First of all, thank you for doing all of this rewrite. I really appreciate it and it looks great so far.

A couple of pieces that I wanted to raise here:

  1. Backwards compatibility
    I've re-read the code for the current version of this GitHub Action and the desktop app integration has never been an authentication method supported by this action. It will error if neither of the connect nor service account environment variables are configured. This should, therefore, smoothen this migration to a pure JS action and significantly decrease the risk of this not being backwards compatible.

  2. Windows support
    Since with this change we can (finally) add support for Windows runners, would you mind adding this OS in the test.yml file in the os matrix? It should look like this:

    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        auth: [connect, service-account]
        exclude:
          - os: macos-latest
            auth: connect
          - os: windows-latest
            auth: connect
  3. Code review
    The overall approach of the changes in this PR look really good. A couple of them are nitpicks and improvements in naming and such, but a couple of key areas to look at are:

    • erroring when duplicate fields are found.
    • identifying environment variables that have secret references and validating them.
    • simplifying the code for validating the authentication and building the client that will resolve the secret references.

src/service/connect/index.ts Outdated Show resolved Hide resolved
src/service/account.ts Outdated Show resolved Hide resolved
__tests__/connect.test.ts Outdated Show resolved Hide resolved
__tests__/main.test.ts Outdated Show resolved Hide resolved
__tests__/main.test.ts Outdated Show resolved Hide resolved
__tests__/utils.test.ts Outdated Show resolved Hide resolved
__tests__/index.test.ts Outdated Show resolved Hide resolved
src/utils.ts Outdated Show resolved Hide resolved
src/service/account.ts Outdated Show resolved Hide resolved
src/main.ts Outdated Show resolved Hide resolved
Copy link
Member

@edif2008 edif2008 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional review: ❌

I've run this version of the action on happy and unhappy paths.

One regression with this implementation is that the following scenario now fails in both Connect and service account authentications:
An item with two fields secret label twice: one which isn't in any sections, and one that is in a section. Their secret references would look as follows:

  • Field in no section: op://vault/item/my-secret
  • Field in section: op://vault/item/section/my-secret

Both should return the value. However, in this rewrite, the first one will fail with the following error:

error resolving secret reference: more than one field matched the secret reference

We've noticed that for the SDK, that is a bug and we're working on fixing it. For the Connect client implementation, that can be addressed as part of this PR.

Code review: ✅

This looks even better. I like the direction of this rewrite. The last step is formatting the code and making it lint-compliant.

Other notes

Windows support

It seems that testing the pipeline against the Windows runners unveiled that this migration is not fully ready for Windows support. Two key pieces that are currently blocking this:

  • Configuration action: This piece is currently written in shell script, which is not recognized in Windows runners.
  • Pipeline configuration: Integration tests run shell scripts, which need to be rewritten in the case of Windows runners.

Therefore, to not increase the complexity of this PR, I'd like to ask you to remove the Windows runner from the pipeline and we will address these in a separate PR. For simplicity, reverting bad9d5c will suffice. Apologies for the inconvenience.

Running tests pipeline

It seems that the test pipeline didn't execute on your branch. I have #87 open which should address this.

Linting

Unfortunately, the lint pipeline doesn't verify for this, and we will address it with #88.

In the meantime, I'm working on improving the linter so that you can also check for any issues on your PR and address them accordingly (as part of #83).

import { OPConnect } from "@1password/connect/dist/lib/op-connect";
import { FullItem } from "@1password/connect/dist/model/fullItem";

describe("test connect with different secret refs", () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One test worth adding is getting a field that is in a section, but is unique in the item like so: op://vault/item/field. This one should also succeed in getting the field.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Item JSON
{
    "overview": {
        "title": "GitHub Action Test Bak",
        "ainfo": ""
    },
    "details": {
        "notesPlain": "",
        "sections": [
            {
                "name": "2cvyorng3vnw6li2xnmgr2bvsa",
                "title": "section",
                "fields": [
                    {
                        "t": "text",
                        "n": "pke3kfordlgadlk5ezjxfgpxra",
                        "k": "string",
                        "v": "`section/text`",
                        "a": {
                            "multiline": "yes"
                        },
                        "inputTraits": {
                            "autocorrection": "no",
                            "autocapitalization": "Sentences"
                        }
                    }
                ]
            },
            {
                "name": "4jng3nm7yzsijlrs5u47law3fi",
                "title": "add more",
                "fields": [
                    {
                        "t": "text",
                        "n": "ldzsoyrn54ntzm3xb6m5lhedoi",
                        "k": "string",
                        "v": "`add more/text`",
                        "a": {
                            "multiline": "yes"
                        },
                        "inputTraits": {
                            "autocorrection": "no",
                            "autocapitalization": "Sentences"
                        }
                    },
                    {
                        "t": "cs",
                        "n": "z3fmurrywq3hreerkejaxtx4ai",
                        "k": "string",
                        "v": "`add more/cs`",
                        "a": {
                            "multiline": "yes"
                        },
                        "inputTraits": {
                            "autocorrection": "no",
                            "autocapitalization": "Sentences"
                        }
                    }
                ]
            },
            {
                "name": "add more",
                "title": "",
                "fields": [
                    {
                        "t": "text",
                        "n": "ft5nzrsp3p6xsezeffiyizb7hu",
                        "k": "string",
                        "v": "hello world",
                        "a": {
                            "multiline": "yes"
                        },
                        "inputTraits": {
                            "autocorrection": "no",
                            "autocapitalization": "Sentences"
                        }
                    }
                ]
            }
        ]
    },
    "createdAt": "2024-11-05T14:54:49Z",
    "updatedAt": "2024-12-17T04:54:09Z",
    "faveIndex": 0,
    "trashed": "N",
    "templateUuid": "003",
    "uuid": "5vtojw237m2cxymfkfhtxsrazm"
}
Item Screeshot image
  • CLI: op --version 2.30.3
  • SDK: @1password/sdk 0.1.5
secret_reference SDK CLI
"op://dev/GitHub Action Test Bak/section/text" section/text section/text
"op://dev/GitHub Action Test Bak/add more/text" ❌resolving secret reference: more than one section matched the secret reference ❌could not read secret 'op://dev/GitHub Action Test Bak/add more/text': The item has more than one "add more.text" field. Include the section name to specify which field to modify: '
.='.
"op://dev/GitHub Action Test Bak/add more/cs" ❌resolving secret reference: more than one section matched the secret reference add more/cs
"op://dev/GitHub Action Test Bak/text" ❌resolving secret reference: more than one field matched the secret reference hello world
"op://dev/GitHub Action Test Bak/cs" add more/cs add more/cs

Note

section named add more on purpose

bit confused about:

  • "op://dev/GitHub Action Test Bak/section/text" work but "op://dev/GitHub Action Test Bak/add more/text" error in CLI
  • "op://dev/GitHub Action Test Bak/cs" and "op://dev/GitHub Action Test Bak/text" both work in CLI

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

according spec op://<vault-name>/<item-name>/[section-name/]<field-name>1(section-name is optional), I think using

  • "op://dev/GitHub Action Test Bak/section/text"
  • "op://dev/GitHub Action Test Bak/add more/text"
  • "op://dev/GitHub Action Test Bak/text"

should return an error to introduce the user using unique UUID

Footnotes

  1. https://developer.1password.com/docs/cli/secret-references/

__tests__/utils.test.ts Outdated Show resolved Hide resolved
__tests__/utils.test.ts Outdated Show resolved Hide resolved
@edif2008
Copy link
Member

edif2008 commented Dec 17, 2024

Hey @bxb100,
The contribution you’ve made so far has been amazing. While functionally testing this, I've come across a few feature gaps in the SDK's handling of secret references, differing from the CLI, such as:

  • File support
  • SSH key item support
  • Query parameters - only attribute=otp/totp is supported at the moment in the SDKs.

These features are actively being scoped and prioritized for future updates to the SDK.
Because of that, however, we can’t guarantee at this point the full backwards-compatibility of this rewritten version of the action until those features are available in the SDK as well. Therefore, I propose to move forward with this and make this a v2-alpha until the SDKs have the same support on secret references as the CLI.
Let me know how you feel about this.

@Xuanwo
Copy link

Xuanwo commented Dec 17, 2024

Therefore, I propose to move forward with this and make this a v2-alpha until the SDKs have the same support on secret references as the CLI.

Hi, @edif2008, thank you so much for your review! This idea aligns with my initial thoughts, so here’s my +1 support. I fully support moving quickly rather than causing delays here.

@bxb100
Copy link
Author

bxb100 commented Dec 17, 2024

I have no objections, agree with @Xuanwo

@edif2008
Copy link
Member

Hey @bxb100!

Just wanted to let you know that I've merged a couple of improvements related to the pipelines, as well as the ES Lint tooling. If you could merge the latest version of main into your branch, that would be much appreciated. That will also solve any merge conflicts that currently need to be resolved. 😄

@bxb100
Copy link
Author

bxb100 commented Dec 19, 2024

Done, PTAL @edif2008

@jb1p jb1p mentioned this pull request Dec 26, 2024
for (const envName of envs) {
extractSecret(envName, shouldExportEnv);
for (const key of refs) {
await extractSecret(auth, key, shouldExportEnv);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use await Promise.all() here?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I'm guessing it's not a blocker, right? This PR is quite large now, how about move all non-blocker changes to separate PRs instead?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool

@edif2008 edif2008 mentioned this pull request Dec 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants