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

Support pre and post steps in Composite Actions #1478

Open
umarcor opened this issue Nov 12, 2021 · 29 comments
Open

Support pre and post steps in Composite Actions #1478

umarcor opened this issue Nov 12, 2021 · 29 comments
Labels
enhancement New feature or request future Feature work that we haven't prioritized

Comments

@umarcor
Copy link

umarcor commented Nov 12, 2021

Describe the enhancement

Currently, three Action types are supported: Docker, JavaScript and Composite (see https://docs.github.com/en/actions/creating-actions/about-custom-actions#types-of-actions). However, features pre, pref-if, post and post-if are only is supported in JavaScript Actions only (see https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runs-for-javascript-actions). Therefore, users writing workflows/actions using some scripting language such as Python are forced to wrap the steps in JavaScript in order to register pre and post steps. See, for example, https://github.com/pyTooling/Actions/tree/main/with-post-step:

name: With post step
description: 'Generic JS Action to execute a main command and set a command in a post step.'
inputs:
  main:
    description: 'Main command/script.'
    required: true
  post:
    description: 'Post command/script.'
    required: true
  key:
    description: 'Name of the state variable used to detect the post step.'
    required: false
    default: POST
runs:
  using: 'node12'
  main: 'main.js'
  post: 'main.js'
const { exec } = require('child_process');

function run(cmd) {
  exec(cmd, (error, stdout, stderr) => {
    if ( stdout.length != 0 ) { console.log(`${stdout}`); }
    if ( stderr.length != 0 ) { console.error(`${stderr}`); }
    if (error) {
      process.exitCode = error.code;
      console.error(`${error}`);
    }
  });
}

const key = process.env.INPUT_KEY.toUpperCase();

if ( process.env[`STATE_${key}`] != undefined ) { // Are we in the 'post' step?
  run(process.env.INPUT_POST);
} else { // Otherwise, this is the main step
  console.log(`::save-state name=${key}::true`);
  run(process.env.INPUT_MAIN);
}

The complexity might be simplified if Python or Bash or PowerShell were supported similarly to JavaScript:

name: 'Login to container registries and set a post step'

runs:
  using: 'python'
  main: 'precmd.py'
  post: 'postcmd.py'

Additional information

Alternatively, since regular steps support Python as a built-in shell already, the same capability might be achieved if Composite Actions supported fields pre and post as a complement to steps. For instance:

name: 'Login to container registries and set a post step'

inputs:
  precmd:
    description: 'Pre command'
    required: true
  postcmd:
    description: 'Pre command'
    required: true

runs:
  using: 'composite'
  pre:
    - shell: python
      run: ${{ inputs.precmd }}
  post:
    - shell: python
      run: ${{ inputs.postcmd }}
  steps:
    - ...

/cc @thboop, per #646 (comment)

@umarcor umarcor added the enhancement New feature or request label Nov 12, 2021
@umarcor umarcor changed the title Support actions of type Script or support pre and post steps in composite actions Support actions of type Script or support pre and post steps in Composite Actions Nov 12, 2021
@umarcor umarcor changed the title Support actions of type Script or support pre and post steps in Composite Actions Support actions of type 'Script'; or support pre and post steps in Composite Actions Nov 12, 2021
@webknjaz
Copy link

I've also faced this need. Subscribing.

@umarcor
Copy link
Author

umarcor commented Nov 22, 2021

@webknjaz you might want to use hdl/containers/utils/with-post-step, or just copy the sources to you own repo: https://github.com/pyTooling/Actions/tree/main/with-post-step. For instance:

  - name: Release
    uses: ./utils/with-post-step
    with:
      main: |
        echo '${{ inputs.gcr_token }}' | docker login gcr.io -u _json_key --password-stdin
        echo '${{ inputs.gh_token }}' | docker login ghcr.io -u gha --password-stdin
        echo '${{ inputs.docker_pass }}' | docker login docker.io -u '${{ inputs.docker_user }}' --password-stdin
        dockerRelease ${{ inputs.architecture }} ${{ inputs.collection }} ${{ inputs.images }}
      post: for registry in gcr.io ghcr.io docker.io; do docker logout "$registry"; done

@webknjaz
Copy link

@umarcor I was considering something like this. Will it run at the end of the job execution or at the composite action exit?

@umarcor
Copy link
Author

umarcor commented Nov 22, 2021

When I use it in a composite action, I call that action once only. Therefore, there is no difference between the end of the composite action or the end of the job. However, according to documentation, all post steps are executed at the end of the job, in reverse order.

@ethomson ethomson changed the title Support actions of type 'Script'; or support pre and post steps in Composite Actions Support pre and post steps in Composite Actions Dec 17, 2021
@ethomson
Copy link
Contributor

Supporting pre and post in composite actions seems like a usable addition. We don't have this on our roadmap at the moment, but I'll add it to the list for future work. Thanks for the suggestion!

@jessehouwing
Copy link

jessehouwing commented Mar 11, 2022

I have a use-case for this as well having a runs-post: | block would also work for me, that's basically the trick these custom post-step actions employ. But then you get to pick the kind of script host and add environment variables and such.

In this case I want to inject a task at the end of the workflow that keeps the runner alive a bit longer for debugging purposes.

Adding a wait at the end works as a workaround for now:

- run: |
        Start-Sleep -seconds 300
      shell: pwsh

@jessehouwing
Copy link

@webknjaz you might want to use hdl/containers/utils/with-post-step, or just copy the sources to you own repo: https://github.com/pyTooling/Actions/tree/main/with-post-step. For instance:

  - name: Release
    uses: ./utils/with-post-step
    with:
      main: |
        echo '${{ inputs.gcr_token }}' | docker login gcr.io -u _json_key --password-stdin
        echo '${{ inputs.gh_token }}' | docker login ghcr.io -u gha --password-stdin
        echo '${{ inputs.docker_pass }}' | docker login docker.io -u '${{ inputs.docker_user }}' --password-stdin
        dockerRelease ${{ inputs.architecture }} ${{ inputs.collection }} ${{ inputs.images }}
      post: for registry in gcr.io ghcr.io docker.io; do docker logout "$registry"; done

Anyone know what he magic syntax would be to include such local action in the composite action repo?

    - name: Wait for user to terminate workflow
      uses: ./with-post-step
      with:

in the action YAML will be relative to therepo root, not relative to the composite's action.yaml file.

@umarcor
Copy link
Author

umarcor commented Mar 11, 2022

Maybe uses: ${{ github.action_path }}/with-post-step? (I did not try it)

@jessehouwing
Copy link

jessehouwing commented Mar 11, 2022

Tried that. Not making actions happy:

Error: jessehouwing/debug-via-ssh/main/action.yaml (Line: 154, Col: 13):
Error: jessehouwing/debug-via-ssh/main/action.yaml (Line: 154, Col: 13): Unrecognized named-value: 'github'. Located at position 1 within expression: github.action_path
Error: jessehouwing/debug-via-ssh/main/action.yaml (Line: 154, Col: 13): Expected format {org}/{repo}[/path]@ref. Actual '${{ github.action_path }}/with-post-step'
Error: System.FormatException: Input string was not in a correct format.

For now fixed by passing in the full action path:

uses: jessehouwing/debug-via-ssh/with-post-step@main

But I consider this a bug... I'd expect a composite action to be able to reference its own local actions.

@umarcor
Copy link
Author

umarcor commented Mar 11, 2022

@jessehouwing see community/community#9049 (via hdl/containers#48).

@Kurt-von-Laven
Copy link

This would make it a lot easier to create quick and easy actions that cache things for various common tools/languages. Excited to hear it's already on the roadmap!

@menasheh
Copy link

menasheh commented May 3, 2022

When is this planned? Seems like a fundamental feature to me

@harzallah
Copy link

+1

@noahsbwilliams
Copy link

Just writing in support - would have a lot of value in the context of "write secret --> $action --> remove secret" tasks

@StephenHodgson
Copy link

Please add this functionality, thanks

@fabasoad
Copy link

Supporting pre and post in composite actions seems like a usable addition. We don't have this on our roadmap at the moment, but I'll add it to the list for future work. Thanks for the suggestion!

Hi @ethomson! Do you have any news for us? Had it been added to the roadmap (if yes, maybe you could share the ETA) or not yet? Thanks.

@umarcor
Copy link
Author

umarcor commented Mar 22, 2023

@fabasoad, according to the bio (https://github.com/ethomson) he does not work for GitHub anymore. You might want to ping/ask @chrispat and/or @TingluoHuang.

@antoineco
Copy link

antoineco commented Jun 11, 2023

@Himura2la everybody here wants to see this happen as much as you do. Until this happens, you can use this (as already suggested) so that you don't have to write any Javascript yourself:

    - uses: pyTooling/Actions/with-post-step@v0.4.5
      with:
        main: |
          main shell commands
        post: |
          post execution shell commands

Example of usage here

@umarcor
Copy link
Author

umarcor commented Jun 11, 2023

It's the same as the action shared by @antoineco but doesn't require a public action - some companies have restrictions on public actions.

For completeness, they can copy the sources to their repo and use it either internally or locally. See https://github.com/pyTooling/Actions/tree/main/with-post-step. It's 10 lines of Apache licensed code.

@philomory
Copy link

The with-post-step action is a useful stopgap, but, besides not being as "clean" as a native solution, it's worth pointing out that there are actually situations where it doesn't actually work (and as far as I know, could not be made to work); specifically, if you want to schedule an action (rather than a shell script) to run as post-step of your composite action, with-post-step won't actually help.

The place that this has come up most frequently for me is wanting to have a composite action that executes actions/cache/restore as part of the composite action, and then schedules actions/cache/save as a post-run step (that runs at the end of the calling workflow, not at the end of the composite action). Using actions/cache/restore and actions/cache/save individually gives you a lot more control than just using actions/cache (which is why they exist in the first place), but, using them as part of a composite action is pretty unwieldy, and I don't think it's actually possible to fix that through clever workarounds like with-post-step, it's something that would need native support.

@pauldraper
Copy link

pauldraper commented Aug 4, 2023

I came here to solve the exact same problem as @philomory ... ergonomically and conditionally running cache/restore and cache/save.

@sandstrom
Copy link

This would solve a lot of headaches around cache logic and such. Especially since it's not possible to use post steps in Reusable Workflows either.

@zak905
Copy link

zak905 commented Mar 25, 2024

Hello, any updates about this ?

@junaruga
Copy link

junaruga commented Mar 25, 2024

We, the Ruby project, are using the gacts/run-and-post-run GitHub Action as alternative at the commit.

@rrauenza
Copy link

rrauenza commented Jun 11, 2024

Another workaround, add this to the top of your run if it is sh/bash:

- name: run container 
  run: |
      function cleanup() {                                                
          docker image rm "${{ steps.build-image.outputs.image }}" || true        
      }                                                                       
      trap cleanup EXIT   
      docker run "${{ steps.build-image.outputs.image }}"

edit: Can anyone explain the thumbs down? Am I mistaken that this works to achieve a post cleanup that always runs?

@jaymecd
Copy link

jaymecd commented Jun 12, 2024

trap works within same shell.

so this is almost equivalent (except for run fails):

      docker run "${{ steps.build-image.outputs.image }}"
      docker image rm "${{ steps.build-image.outputs.image }}" || true        

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request future Feature work that we haven't prioritized
Projects
None yet
Development

No branches or pull requests