Skip to content

How to reference a separate script file which processes and returns output? #438

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

Closed
rdhar opened this issue Nov 17, 2023 Discussed in #433 · 4 comments
Closed

How to reference a separate script file which processes and returns output? #438

rdhar opened this issue Nov 17, 2023 Discussed in #433 · 4 comments

Comments

@rdhar
Copy link

rdhar commented Nov 17, 2023

Discussed in #433

Originally posted by rdhar November 13, 2023
Hi, I would like to strip out snippets of JS code into separate files outside of YAML for ease of legibility. However, I'm struggling to understand how best to achieve something this.

Before/Current
script: |
    const { data: list_comments } = await github.rest.issues.listComments({
    issue_number: context.issue.number,
    owner: context.repo.owner,
    per_page: 100,
    repo: context.repo.repo,
  });
  const get_comment = list_comments
    .sort((a, b) => b.id - a.id)
    .find((comment) => /^keyword/.test(comment.body));
  return {
    body: get_comment.body,
    id: get_comment.id,
  };
After/Proposed
script: |
  require(process.env.GITHUB_ACTION_PATH + '/comment.js');
// File: comment.js
const { data: list_comments } = await github.rest.issues.listComments({
  issue_number: context.issue.number,
  owner: context.repo.owner,
  per_page: 100,
  repo: context.repo.repo,
});
const get_comment = list_comments
  .sort((a, b) => b.id - a.id)
  .find((comment) => /^keyword/.test(comment.body));
return {
  body: get_comment.body,
  id: get_comment.id,
};

With this, I get: "SyntaxError: await is only valid in async functions and the top level bodies of modules."
If I drop the await, then I get: "ReferenceError: github is not defined."

I'm sure I'm missing something obvious with module.exports = ({ github, context }) => { ... }, but I'm not sure how best to address this particular script which: makes an API call, processes the response, and returns the output in that specific order.

Really appreciate any thoughts/inputs, thanks for your time.

@kilianc
Copy link

kilianc commented Dec 2, 2023

I would argue that first class external script support is a desirable feature! The default is to support await in code assigned to script, there is an eventual need to move the script into a file, and the likely expectation is to copy paste the content to a file and everything works exactly the same.

The workaround I have for now is:

...
  script: |
    const fn = require('${{ github.workspace }}/.github/fn.js')
    await fn({
      github,
      context,
      core,
      glob,
      io,
      exec,
      require
    })

or

...
  script: await require('${{ github.workspace }}/.github/fn.js')({ github, context, core, glob, io, exec, require })

@yhakbar
Copy link

yhakbar commented Feb 14, 2024

In this example, I believe you would do something like the following, per the docs for async here:

script: |
  const script = require(process.env.GITHUB_ACTION_PATH + '/comment.js');
  await script({github, context, core})
// File: comment.js
module.exports = async ({ github, context, core  }) => {
  const { data: list_comments } = await github.rest.issues.listComments({
    issue_number: context.issue.number,
    owner: context.repo.owner,
    per_page: 100,
    repo: context.repo.repo,
  });
  const get_comment = list_comments
    .sort((a, b) => b.id - a.id)
    .find((comment) => /^keyword/.test(comment.body));
  return {
    body: get_comment.body,
    id: get_comment.id,
  };
}

If you have more that you need to do, you can require more scripts and await them after calling the first one, etc.

@rdhar
Copy link
Author

rdhar commented Feb 14, 2024

I would argue that first class external script support is a desirable feature! The default is to support await in code assigned to script, there is an eventual need to move the script into a file, and the likely expectation is to copy paste the content to a file and everything works exactly the same.

Great to hear your support, @kilianc, and agree that first-class support for external script files would be ideal to match parity with run: bash script.sh per GitHub docs.

@rdhar
Copy link
Author

rdhar commented Feb 20, 2024

In this example, I believe you would do something like the following, per the docs for async here:
If you have more that you need to do, you can require more scripts and await them after calling the first one, etc.

Brilliant, thank you for sharing, @yhakbar!

That script setup and module.exports = async... is exactly what was needed -- the only tweak was to replace the return { ... } with core.setOutput(), like so:

// Before
return {
  body: get_comment.body,
  id: get_comment.id,
};

// After
core.setOutput("body", get_comment.body);
core.setOutput("id", get_comment.id);

Similarly, I had to amend references to the output result within the action.yml workflow as well:

# Before
fromJSON(steps.comment.outputs.result)['body']
fromJSON(steps.comment.outputs.result)['id']

# After
steps.comment.outputs.body
steps.comment.outputs.id

To recognize your contribution, would you mind sharing your answer on the original Q&A #433 discussion, where I can accept it as the correct answer?

@rdhar rdhar closed this as completed Feb 20, 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

No branches or pull requests

3 participants