mbox series

[0/2] replacing ci/config/allow-ref with a repo variable

Message ID 20230830194919.GA1709446@coredump.intra.peff.net (mailing list archive)
Headers show
Series replacing ci/config/allow-ref with a repo variable | expand

Message

Jeff King Aug. 30, 2023, 7:49 p.m. UTC
This is a more efficient way to do the same thing that
ci/config/allow-ref does (which didn't exist back then).

We should be able to do the same with ci/config/skip-concurrent (and
just use vars.CI_SKIP_CONCURRENT) throughout the workflow. But I didn't
test that at all.

After that, the only useful thing left in the "config" job would be the
"skip-if-redundant" step. I'm not sure if it will be possible to get the
same behavior there without spinning up a VM.

  [1/2]: ci: allow branch selection through "vars"
  [2/2]: ci: deprecate ci/config/allow-ref script

 .github/workflows/main.yml | 10 +++++++---
 ci/config/README           | 14 ++++++++++++++
 ci/config/allow-ref.sample | 27 ---------------------------
 3 files changed, 21 insertions(+), 30 deletions(-)
 create mode 100644 ci/config/README
 delete mode 100755 ci/config/allow-ref.sample

Comments

Junio C Hamano Aug. 30, 2023, 10:55 p.m. UTC | #1
Jeff King <peff@peff.net> writes:

> This is a more efficient way to do the same thing that
> ci/config/allow-ref does (which didn't exist back then).

Very nice.

> After that, the only useful thing left in the "config" job would be the
> "skip-if-redundant" step. I'm not sure if it will be possible to get the
> same behavior there without spinning up a VM.
>
>   [1/2]: ci: allow branch selection through "vars"
>   [2/2]: ci: deprecate ci/config/allow-ref script
>
>  .github/workflows/main.yml | 10 +++++++---
>  ci/config/README           | 14 ++++++++++++++
>  ci/config/allow-ref.sample | 27 ---------------------------
>  3 files changed, 21 insertions(+), 30 deletions(-)
>  create mode 100644 ci/config/README
>  delete mode 100755 ci/config/allow-ref.sample
Phillip Wood Sept. 1, 2023, 1:24 p.m. UTC | #2
Hi Peff

On 30/08/2023 20:49, Jeff King wrote:
> This is a more efficient way to do the same thing that
> ci/config/allow-ref does (which didn't exist back then).

I like the idea of a more efficient way to skip the ci for certain refs. 
I've got my allow-ref script set up to reject a bunch of refs and run 
the ci on everything else. It's not clear to me how to replicate that 
with the setup proposed here. Would it be possible to add a second 
variable that prevents the ci from being run if it contains ref being 
pushed?

Best Wishes

Phillip

> We should be able to do the same with ci/config/skip-concurrent (and
> just use vars.CI_SKIP_CONCURRENT) throughout the workflow. But I didn't
> test that at all.
> 
> After that, the only useful thing left in the "config" job would be the
> "skip-if-redundant" step. I'm not sure if it will be possible to get the
> same behavior there without spinning up a VM.
> 
>    [1/2]: ci: allow branch selection through "vars"
>    [2/2]: ci: deprecate ci/config/allow-ref script
> 
>   .github/workflows/main.yml | 10 +++++++---
>   ci/config/README           | 14 ++++++++++++++
>   ci/config/allow-ref.sample | 27 ---------------------------
>   3 files changed, 21 insertions(+), 30 deletions(-)
>   create mode 100644 ci/config/README
>   delete mode 100755 ci/config/allow-ref.sample
>
Jeff King Sept. 1, 2023, 5:32 p.m. UTC | #3
On Fri, Sep 01, 2023 at 02:24:59PM +0100, Phillip Wood wrote:

> On 30/08/2023 20:49, Jeff King wrote:
> > This is a more efficient way to do the same thing that
> > ci/config/allow-ref does (which didn't exist back then).
> 
> I like the idea of a more efficient way to skip the ci for certain refs.
> I've got my allow-ref script set up to reject a bunch of refs and run the ci
> on everything else. It's not clear to me how to replicate that with the
> setup proposed here. Would it be possible to add a second variable that
> prevents the ci from being run if it contains ref being pushed?

Drat, I was hoping nobody was using it that way. :)

Yes, I think it would be possible to do something like:

  if: |
    (vars.CI_BRANCHES == '' || contains(vars.CI_BRANCHES, github.ref_name)) &&
    !contains(vars.CI_BRANCHES_REJECT, github.ref_name)

It doesn't allow globbing, though. Do you need that?

-Peff
Phillip Wood Sept. 4, 2023, 9:56 a.m. UTC | #4
On 01/09/2023 18:32, Jeff King wrote:
> On Fri, Sep 01, 2023 at 02:24:59PM +0100, Phillip Wood wrote:
> 
>> On 30/08/2023 20:49, Jeff King wrote:
>>> This is a more efficient way to do the same thing that
>>> ci/config/allow-ref does (which didn't exist back then).
>>
>> I like the idea of a more efficient way to skip the ci for certain refs.
>> I've got my allow-ref script set up to reject a bunch of refs and run the ci
>> on everything else. It's not clear to me how to replicate that with the
>> setup proposed here. Would it be possible to add a second variable that
>> prevents the ci from being run if it contains ref being pushed?
> 
> Drat, I was hoping nobody was using it that way. :)

Sorry to be a pain.

> Yes, I think it would be possible to do something like:
> 
>    if: |
>      (vars.CI_BRANCHES == '' || contains(vars.CI_BRANCHES, github.ref_name)) &&
>      !contains(vars.CI_BRANCHES_REJECT, github.ref_name)
> 
> It doesn't allow globbing, though. Do you need that?

Oh I'd missed that, yes I do. All the globs are prefix matches but I'm 
not sure that helps.

Best Wishes

Phillip

> -Peff
Jeff King Sept. 5, 2023, 7:24 a.m. UTC | #5
On Mon, Sep 04, 2023 at 10:56:15AM +0100, Phillip Wood wrote:

> > Yes, I think it would be possible to do something like:
> > 
> >    if: |
> >      (vars.CI_BRANCHES == '' || contains(vars.CI_BRANCHES, github.ref_name)) &&
> >      !contains(vars.CI_BRANCHES_REJECT, github.ref_name)
> > 
> > It doesn't allow globbing, though. Do you need that?
> 
> Oh I'd missed that, yes I do. All the globs are prefix matches but I'm not
> sure that helps.

It does make it easier. There's no globbing function available to us,
but if we know something is a prefix, there's a startsWith() we can use.
It does seem we're getting a combinatorial expansion of things to check,
though:

  - full names to accept
  - full names to reject
  - prefixes to accept
  - prefixes to reject

I wrote "prefixes" but I'm actually not sure how feasible that is. That
implies iterating over the list of prefixes, which I'm not sure we can
do.

-Peff
Phillip Wood Sept. 7, 2023, 10:04 a.m. UTC | #6
On 05/09/2023 08:24, Jeff King wrote:
> On Mon, Sep 04, 2023 at 10:56:15AM +0100, Phillip Wood wrote:
> 
>>> Yes, I think it would be possible to do something like:
>>>
>>>     if: |
>>>       (vars.CI_BRANCHES == '' || contains(vars.CI_BRANCHES, github.ref_name)) &&
>>>       !contains(vars.CI_BRANCHES_REJECT, github.ref_name)
>>>
>>> It doesn't allow globbing, though. Do you need that?
>>
>> Oh I'd missed that, yes I do. All the globs are prefix matches but I'm not
>> sure that helps.
> 
> It does make it easier. There's no globbing function available to us,
> but if we know something is a prefix, there's a startsWith() we can use.
> It does seem we're getting a combinatorial expansion of things to check,
> though:
> 
>    - full names to accept
>    - full names to reject
>    - prefixes to accept
>    - prefixes to reject
> 
> I wrote "prefixes" but I'm actually not sure how feasible that is. That
> implies iterating over the list of prefixes, which I'm not sure we can
> do.

I scanned the github documentation the other day and wondered if it 
would be possible to use with fromJson with a json array to do a prefx 
match on each element. It all sounds like it is getting a bit 
complicated though.

Best Wishes

Phillip

> -Peff
Jeff King Sept. 11, 2023, 9:36 a.m. UTC | #7
On Thu, Sep 07, 2023 at 11:04:33AM +0100, Phillip Wood wrote:

> I scanned the github documentation the other day and wondered if it would be
> possible to use with fromJson with a json array to do a prefx match on each
> element. It all sounds like it is getting a bit complicated though.

I poked around and I don't think this is possible. You can use
contains() to do a full match of items in a json array, but I don't see
any other way to iterate. What we'd really want is a "map" function, to
do something like:

  map { startsWith(github.ref_name, $1) } fromJSON(vars.CI_CONFIG.allow_prefix)

But no such "map" exists (and I guess we'd need to collapse the result
down to "is any item true", similar to perl's "grep" function).

Do you need multiple prefixes, or would one each of allow/reject be
sufficient? I tried this and it works:

jobs:
  ci-config:
    name: config
    if: |
      (fromJSON(vars.CI_CONFIG).allow == '' ||
       contains(fromJSON(vars.CI_CONFIG).allow, github.ref_name)) &&
      (fromJSON(vars.CI_CONFIG).reject == '' ||
       !contains(fromJSON(vars.CI_CONFIG).reject, github.ref_name)) &&
      (fromJSON(vars.CI_CONFIG).allow-prefix == '' ||
       startsWith(github.ref_name, fromJSON(vars.CI_CONFIG).allow-prefix)) &&
      (fromJSON(vars.CI_CONFIG).reject-prefix == '' ||
       !startsWith(github.ref_name, fromJSON(vars.CI_CONFIG).reject-prefix))

And then various values of CI_CONFIG seem to work:

  - allow explicit branch names ("one" and "two" are run, everything
    else is skipped)

    { "allow": ["one", "two"] }

  - reject explicit names (everything except "three" is skipped)

    { "reject": ["three"] }

  - allow by prefix ("ok/* is run, everything else is skipped)

    { "allow-prefix": "ok/" }

  - reject by prefix (everything except "nope/*" is run)

    { "reject-prefix": "nope/" }

  - every key must approve to run, but missing keys default to running.
    So a reject overrides allow ("ok/one" and "ok/two" run, but
    "ok/three" does not)

    {
      "allow-prefix": "ok/",
      "reject", "ok/three"
    }

I suppose one hacky way to support multiple prefixes is to just unroll
the loop ourselves (since missing keys are ignored). I didn't test it,
but something like:

      (fromJSON(vars.CI_CONFIG).allow-prefix[0] == '' ||
       startsWith(github.ref_name, fromJSON(vars.CI_CONFIG).allow-prefix[0])) &&
      (fromJSON(vars.CI_CONFIG).allow-prefix[1] == '' ||
       startsWith(github.ref_name, fromJSON(vars.CI_CONFIG).allow-prefix[1])) &&
      (fromJSON(vars.CI_CONFIG).allow-prefix[2] == '' ||
       startsWith(github.ref_name, fromJSON(vars.CI_CONFIG).allow-prefix[2])) &&
       ...etc...

Would allow:

  {
    "allow-prefix": [
      "ok/",
      "also-ok/"
    ]
  }

Looking at the ci-config branch of phillipwood/git.git, I see this in
your allow-refs:

      refs/heads/main|refs/heads/master|refs/heads/maint|refs/heads/next|refs/heads/seen|refs/tags/gitgui-*|refs/tags/pr-[0-9]*|refs/tags/v[1-9].*)

So you do use multiple prefixes, though all in refs/tags/.  Do you
actually push tags for which you do want to run CI, or would it be
sufficient to just reject "refs/tags/" completely (though that implies
using the fully qualified ref and not the short name; it would also be
easy to add a boolean "allow tags" config option).

-Peff
Phillip Wood Sept. 13, 2023, 3:16 p.m. UTC | #8
Hi Peff

On 11/09/2023 10:36, Jeff King wrote:
> On Thu, Sep 07, 2023 at 11:04:33AM +0100, Phillip Wood wrote:
> Looking at the ci-config branch of phillipwood/git.git, I see this in
> your allow-refs:
> 
>        refs/heads/main|refs/heads/master|refs/heads/maint|refs/heads/next|refs/heads/seen|refs/tags/gitgui-*|refs/tags/pr-[0-9]*|refs/tags/v[1-9].*)
> 
> So you do use multiple prefixes, though all in refs/tags/.  Do you
> actually push tags for which you do want to run CI,

Yes, but not very often - I could probably just reject all tags and 
start the CI manually when I want it (assuming that's an option). Thanks 
for digging into the various options, it sounds like it is possible so 
long as we don't want multiple prefixes.

Aside: what I'd really like is to be able to set an environment variable 
when I push to skip or force the CI

	GITHUB_SKIP_CI=1 git push github ...

but that would require support from the git client, the protocol and the 
server.

Best Wishes

Phillip
Jeff King Sept. 14, 2023, 12:30 a.m. UTC | #9
On Wed, Sep 13, 2023 at 04:16:48PM +0100, Phillip Wood wrote:

> On 11/09/2023 10:36, Jeff King wrote:
> > On Thu, Sep 07, 2023 at 11:04:33AM +0100, Phillip Wood wrote:
> > Looking at the ci-config branch of phillipwood/git.git, I see this in
> > your allow-refs:
> > 
> >        refs/heads/main|refs/heads/master|refs/heads/maint|refs/heads/next|refs/heads/seen|refs/tags/gitgui-*|refs/tags/pr-[0-9]*|refs/tags/v[1-9].*)
> > 
> > So you do use multiple prefixes, though all in refs/tags/.  Do you
> > actually push tags for which you do want to run CI,
> 
> Yes, but not very often - I could probably just reject all tags and start
> the CI manually when I want it (assuming that's an option). Thanks for
> digging into the various options, it sounds like it is possible so long as
> we don't want multiple prefixes.

I'll additionally have to switch to using the full refname in the
matches, but that is probably a reasonable thing to do anyway (it makes
config a bit more verbose, but it is obviously much more flexible).

We can do the loop-unroll thing if we really want to support multiple
prefixes, but if you're OK with it, let's try the single-prefix way and
see if anybody runs into problems (I'm still convinced there's only a
few of us using this stuff anyway). I'm hesitant to do the unroll just
because it requires picking a maximum value, with a bizarre failure if
you happen to have 4 prefixes or whatever.

I'll see if I can polish up what I showed earlier into a patch.

(BTW, one other thing I tried was using fromJSON(vars.CI_CONFIG) in the
"on" expression, which in theory would allow globbing. But it doesn't
look like expressions work at all in that context. And even if they did,
I'm not sure we could make it work such that an empty CI_CONFIG used
some defaults, since there's no conditional-expression ternary
operator).

> Aside: what I'd really like is to be able to set an environment variable
> when I push to skip or force the CI
> 
> 	GITHUB_SKIP_CI=1 git push github ...
> 
> but that would require support from the git client, the protocol and the
> server.

We have the necessary bits at the protocol: push-options. But it's up to
the server-side hooks to decide which ones are meaningful and to do
something useful with them. It looks like GitLab supports:

  git push -o ci.skip=1 ...

but I don't think GitHub respects any equivalent option.

-Peff
Jeff King Sept. 14, 2023, 12:44 a.m. UTC | #10
Two minor corrections in what I wrote...

On Wed, Sep 13, 2023 at 08:30:10PM -0400, Jeff King wrote:

> (BTW, one other thing I tried was using fromJSON(vars.CI_CONFIG) in the
> "on" expression, which in theory would allow globbing. But it doesn't
> look like expressions work at all in that context. And even if they did,
> I'm not sure we could make it work such that an empty CI_CONFIG used
> some defaults, since there's no conditional-expression ternary
> operator).

It's not a real ?: operator, but GitHub does use the word "ternary" to
describe the short-circuit behavior of:

  condition && "if-true" || "if-not-true"

So in theory we could do:

  on: ${{ fromJSON(var.CI_ON != '' && vars.CI_ON || "[pull_request, push]" }}

but I couldn't make it work (it complains about the value of "on" in
such a way that I assume it is simply not expanding expressions at all).

> We have the necessary bits at the protocol: push-options. But it's up to
> the server-side hooks to decide which ones are meaningful and to do
> something useful with them. It looks like GitLab supports:
> 
>   git push -o ci.skip=1 ...
> 
> but I don't think GitHub respects any equivalent option.

The syntax here is actually just "git push -o ci.skip", without the
equals.

-Peff
Phillip Wood Sept. 14, 2023, 10:49 a.m. UTC | #11
On 14/09/2023 01:30, Jeff King wrote:
> On Wed, Sep 13, 2023 at 04:16:48PM +0100, Phillip Wood wrote:
> We can do the loop-unroll thing if we really want to support multiple
> prefixes, but if you're OK with it, let's try the single-prefix way and
> see if anybody runs into problems (I'm still convinced there's only a
> few of us using this stuff anyway). I'm hesitant to do the unroll just
> because it requires picking a maximum value, with a bizarre failure if
> you happen to have 4 prefixes or whatever.

Lets start with a single-prefix and see if anyone complains

>> Aside: what I'd really like is to be able to set an environment variable
>> when I push to skip or force the CI
>>
>> 	GITHUB_SKIP_CI=1 git push github ...
>>
>> but that would require support from the git client, the protocol and the
>> server.
> 
> We have the necessary bits at the protocol: push-options. But it's up to
> the server-side hooks to decide which ones are meaningful and to do
> something useful with them. It looks like GitLab supports:
> 
>    git push -o ci.skip=1 ...

Oh I didn't know about that

> but I don't think GitHub respects any equivalent option.

That's a shame

Best Wishes

Phillip