summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/lifecycle.yml189
-rw-r--r--.github/workflows/trunk.yml32
-rw-r--r--cache/repo_cache_test.go276
-rw-r--r--misc/completion/bash/git-bug6
-rw-r--r--misc/completion/generate.go6
-rw-r--r--nix/checks/pinact.nix15
6 files changed, 189 insertions, 335 deletions
diff --git a/.github/workflows/lifecycle.yml b/.github/workflows/lifecycle.yml
deleted file mode 100644
index 1d06ebd4..00000000
--- a/.github/workflows/lifecycle.yml
+++ /dev/null
@@ -1,189 +0,0 @@
----
-name: lifecycle
-
-on:
- issues:
- types:
- - assigned
- - closed
- - demilestoned
- - milestoned
- - pinned
- - unpinned
- - reopened
- issue_comment:
- types:
- - created
- pull_request:
- types:
- - assigned
- - auto_merge_enabled
- - converted_to_draft
- - demilestoned
- - enqueued
- - milestoned
- - ready_for_review
- - reopened
- - synchronize
- pull_request_review:
- types:
- - submitted
- - dismissed
- schedule:
- - cron: '17 3 * * *' # every day at 03:17 UTC
-
-concurrency:
- group: lifecycle-${{ github.event.issue.number || github.event.pull_request.number || 'scheduled' }}
- cancel-in-progress: false
-
-jobs:
- auto-label:
- name: auto-label
- if: github.event_name != 'schedule'
- runs-on: ubuntu-latest
- permissions:
- contents: read
- issues: write
- pull-requests: write
- steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- with:
- sparse-checkout: |
- .github/actions/auto-label
-
- - name: "restore cache: node modules"
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
- with:
- path: .github/actions/auto-label/node_modules
- key: auto-label-${{ runner.os }}-${{ hashFiles('.github/actions/auto-label/package-lock.json') }}
- restore-keys: |
- auto-label-${{ runner.os }}-
-
- - name: "install dependencies for //.github/actions/auto-label"
- working-directory: .github/actions/auto-label
- run: npm ci
-
- - name: "add label for pinned or milestoned item: lifecycle/pinned"
- if: github.event.action == 'pinned' || github.event.action == 'milestoned'
- uses: ./.github/actions/auto-label
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- add: lifecycle/pinned
- remove: |
- lifecycle/idle
- lifecycle/dormant
-
- - name: "remove label for unpinned or demilestoned item: lifecycle/pinned"
- if: github.event.action == 'unpinned' || github.event.action == 'demilestoned'
- uses: ./.github/actions/auto-label
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- remove: lifecycle/pinned
-
- - name: "remove lifecycle label due to activity"
- if: github.event.action != 'pinned' && github.event.action != 'milestoned'
- uses: ./.github/actions/auto-label
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- remove: |
- lifecycle/idle
- lifecycle/dormant
-
- idle-sweeper:
- name: idle-sweeper
- if: github.event_name == 'schedule'
- runs-on: ubuntu-latest
- permissions:
- issues: write
- pull-requests: write
- env:
- ISSUE_MESSAGE_DETAILS: |
- <br /><strong>Issues will <em>not</em> be automatically closed due to
- inactivity.</strong>
- <hr />
- <details>
- <summary>Learn more</summary>
-
- This bot triages issues in order to help the maintainers identify what
- needs attention, according to the following lifecycle rules:
-
- - After 90 days of inactivity, <code>lifecycle/idle</code> is applied
- - After 180 days of inactivity, <code>lifecycle/dormant</code> is applied
-
- The following actions remove the inactive status:
-
- - Removing the <code>lifecycle/*</code> label from this issue
- - Commenting on this issue
- - Adding a new assignee to this issue
- - Reopening this issue
- - Adding this issue to a milestone
- - Pinning this issue to the top of the issue board
-
- To avoid automatic lifecycle management of this issue, maintainers can
- add the <code>lifecycle/pinned</code> label.
- </details>
- PR_MESSAGE_DETAILS: |
- <br /><strong>Pull requests will <em>not</em> be closed automatically
- due to inactivity.</strong>
-
- <details>
- <summary>Learn more</summary>
- This bot triages pull requests in order to help the maintainers identify
- what needs attention, according to the following lifecycle rules:
-
- - After 90 days of inactivity, <code>lifecycle/idle</code> is applied
- - After 90 days of inactivity, <code>lifecycle/dormant</code> is applied
-
- The following actions remove the inactive status:
-
- - Removing the <code>lifecycle/*</code> label from this pull request
- - Commenting on this pull request
- - Submitting a review for this pull request
- - Adding a new assignee to this pull request
- - Reopening this pull request
- - Adding this pull request to a milestone
- - Converting this pull request to a draft
- - Setting this pull request as "ready to review" (not a draft)
- - Enabling auto-merge, or adding this pull request to the merge queue
-
- To avoid automatic lifecycle management of this pull request,
- maintainers can add the <code>lifecycle/pinned</code> label.
- </details>
- steps:
- - name: "inactivity check: 90 days"
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
- with:
- days-before-close: -1
- days-before-stale: 90
- exempt-all-milestones: true
- exempt-issue-labels: lifecycle/idle,lifecycle/dormant,lifecycle/pinned
- exempt-pr-labels: lifecycle/idle,lifecycle/dormant,lifecycle/pinned
- labels-to-remove-when-stale: lifecycle/active
- stale-issue-label: lifecycle/idle
- stale-issue-message: |
- Pinging maintainers. This issue has been inactive for 90 days.
- ${{ env.ISSUE_MESSAGE_DETAILS }}
- stale-pr-label: lifecycle/idle
- stale-pr-message: |
- Pinging maintainers. This pull request has been inactive for 90 days.
- ${{ env.PR_MESSAGE_DETAILS }}
-
- - name: "inactivity check: 180 days"
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
- with:
- days-before-close: -1
- days-before-stale: 180
- exempt-all-milestones: true
- labels-to-remove-when-stale: lifecycle/idle
- only-labels: lifecycle/idle
- stale-issue-label: lifecycle/dormant
- stale-issue-message: |-
- Pinging maintainers. This issue has been inactive for 180 days.
- ${{ env.ISSUE_MESSAGE_DETAILS }}
- stale-pr-label: lifecycle/dormant
- stale-pr-message: |-
- Pinging maintainers. This pull request has been inactive for 180 days.
- ${{ env.PR_MESSAGE_DETAILS }}
diff --git a/.github/workflows/trunk.yml b/.github/workflows/trunk.yml
index 6abf3ed4..7e5243ac 100644
--- a/.github/workflows/trunk.yml
+++ b/.github/workflows/trunk.yml
@@ -43,3 +43,35 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }}
comment-on-alert: true
auto-push: true
+
+ mirror:
+ if: >
+ github.ref == 'refs/heads/master' && github.repository == 'git-bug/git-bug' && github.run_attempt == '1'
+ permissions:
+ contents: write
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ fetch-depth: 0
+
+ # we use a custom deploy key in order to allow the workflow to bypass
+ # branch protection rules. without this, pushing will be rejected
+ - name: setup authentication
+ env:
+ SSH_AUTH_SOCK: /tmp/ssh-agent.sock
+ run: |
+ mkdir -p ~/.ssh
+ echo "${{ secrets.TRUNK_MIRROR_KEY }}" > ~/.ssh/id_rsa
+ chmod 0600 ~/.ssh/id_rsa
+ ssh-agent -a $SSH_AUTH_SOCK > /dev/null
+ ssh-add ~/.ssh/id_rsa
+
+ - name: push to trunk
+ env:
+ SSH_AUTH_SOCK: /tmp/ssh-agent.sock
+ run: |-
+ git config user.name git-bug-bot
+ git config user.email no-reply@git-bug.org
+ git remote set-url origin git@github.com:git-bug/git-bug.git
+ git push --atomic origin HEAD:refs/heads/trunk
diff --git a/cache/repo_cache_test.go b/cache/repo_cache_test.go
index 48704783..e974a3c5 100644
--- a/cache/repo_cache_test.go
+++ b/cache/repo_cache_test.go
@@ -11,169 +11,175 @@ import (
"github.com/git-bug/git-bug/entities/bug"
"github.com/git-bug/git-bug/entities/identity"
"github.com/git-bug/git-bug/entity"
+ "github.com/git-bug/git-bug/internal/test"
"github.com/git-bug/git-bug/query"
"github.com/git-bug/git-bug/repository"
)
func TestCache(t *testing.T) {
- repo := repository.CreateGoGitTestRepo(t, false)
-
- indexCount := func(t *testing.T, name string) uint64 {
- t.Helper()
-
- idx, err := repo.GetIndex(name)
- require.NoError(t, err)
- count, err := idx.DocCount()
- require.NoError(t, err)
-
- return count
- }
+ f := test.NewFlaky(t, &test.FlakyOptions{
+ MaxAttempts: 5,
+ })
- cache, err := NewRepoCacheNoEvents(repo)
- require.NoError(t, err)
+ f.Run(func(t testing.TB) {
+ repo := repository.CreateGoGitTestRepo(t, false)
+ indexCount := func(t testing.TB, name string) uint64 {
+ t.Helper()
- // Create, set and get user identity
- iden1, err := cache.Identities().New("René Descartes", "rene@descartes.fr")
- require.NoError(t, err)
- err = cache.SetUserIdentity(iden1)
- require.NoError(t, err)
- userIden, err := cache.GetUserIdentity()
- require.NoError(t, err)
- require.Equal(t, iden1.Id(), userIden.Id())
+ idx, err := repo.GetIndex(name)
+ require.NoError(t, err)
+ count, err := idx.DocCount()
+ require.NoError(t, err)
- // it's possible to create two identical identities
- iden2, err := cache.Identities().New("René Descartes", "rene@descartes.fr")
- require.NoError(t, err)
+ return count
+ }
- // Two identical identities yield a different id
- require.NotEqual(t, iden1.Id(), iden2.Id())
+ cache, err := NewRepoCacheNoEvents(repo)
+ require.NoError(t, err)
- // There is now two identities in the cache
- require.Len(t, cache.Identities().AllIds(), 2)
- require.Len(t, cache.identities.excerpts, 2)
- require.Len(t, cache.identities.cached, 2)
- require.Equal(t, uint64(2), indexCount(t, identity.Namespace))
- require.Equal(t, uint64(0), indexCount(t, bug.Namespace))
+ // Create, set and get user identity
+ iden1, err := cache.Identities().New("René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+ err = cache.SetUserIdentity(iden1)
+ require.NoError(t, err)
+ userIden, err := cache.GetUserIdentity()
+ require.NoError(t, err)
+ require.Equal(t, iden1.Id(), userIden.Id())
- // Create a bug
- bug1, _, err := cache.Bugs().New("title", "message")
- require.NoError(t, err)
+ // it's possible to create two identical identities
+ iden2, err := cache.Identities().New("René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
- // It's possible to create two identical bugs
- bug2, _, err := cache.Bugs().New("title", "marker")
- require.NoError(t, err)
+ // Two identical identities yield a different id
+ require.NotEqual(t, iden1.Id(), iden2.Id())
- // two identical bugs yield a different id
- require.NotEqual(t, bug1.Id(), bug2.Id())
+ // There is now two identities in the cache
+ require.Len(t, cache.Identities().AllIds(), 2)
+ require.Len(t, cache.identities.excerpts, 2)
+ require.Len(t, cache.identities.cached, 2)
+ require.Equal(t, uint64(2), indexCount(t, identity.Namespace))
+ require.Equal(t, uint64(0), indexCount(t, bug.Namespace))
- // There is now two bugs in the cache
- require.Len(t, cache.Bugs().AllIds(), 2)
- require.Len(t, cache.bugs.excerpts, 2)
- require.Len(t, cache.bugs.cached, 2)
- require.Equal(t, uint64(2), indexCount(t, identity.Namespace))
- require.Equal(t, uint64(2), indexCount(t, bug.Namespace))
+ // Create a bug
+ bug1, _, err := cache.Bugs().New("title", "message")
+ require.NoError(t, err)
- // Resolving
- _, err = cache.Identities().Resolve(iden1.Id())
- require.NoError(t, err)
- _, err = cache.Identities().ResolveExcerpt(iden1.Id())
- require.NoError(t, err)
- _, err = cache.Identities().ResolvePrefix(iden1.Id().String()[:10])
- require.NoError(t, err)
+ // It's possible to create two identical bugs
+ bug2, _, err := cache.Bugs().New("title", "marker")
+ require.NoError(t, err)
- _, err = cache.Bugs().Resolve(bug1.Id())
- require.NoError(t, err)
- _, err = cache.Bugs().ResolveExcerpt(bug1.Id())
- require.NoError(t, err)
- _, err = cache.Bugs().ResolvePrefix(bug1.Id().String()[:10])
- require.NoError(t, err)
+ // two identical bugs yield a different id
+ require.NotEqual(t, bug1.Id(), bug2.Id())
- // Querying
- q, err := query.Parse("status:open author:descartes sort:edit-asc")
- require.NoError(t, err)
- res, err := cache.Bugs().Query(q)
- require.NoError(t, err)
- require.Len(t, res, 2)
+ // There is now two bugs in the cache
+ require.Len(t, cache.Bugs().AllIds(), 2)
+ require.Len(t, cache.bugs.excerpts, 2)
+ require.Len(t, cache.bugs.cached, 2)
+ require.Equal(t, uint64(2), indexCount(t, identity.Namespace))
+ require.Equal(t, uint64(2), indexCount(t, bug.Namespace))
- q, err = query.Parse("status:open marker") // full-text search
- require.NoError(t, err)
- res, err = cache.Bugs().Query(q)
- require.NoError(t, err)
- require.Len(t, res, 1)
+ // Resolving
+ _, err = cache.Identities().Resolve(iden1.Id())
+ require.NoError(t, err)
+ _, err = cache.Identities().ResolveExcerpt(iden1.Id())
+ require.NoError(t, err)
+ _, err = cache.Identities().ResolvePrefix(iden1.Id().String()[:10])
+ require.NoError(t, err)
- // Close
- require.NoError(t, cache.Close())
- require.Empty(t, cache.bugs.cached)
- require.Empty(t, cache.bugs.excerpts)
- require.Empty(t, cache.identities.cached)
- require.Empty(t, cache.identities.excerpts)
+ _, err = cache.Bugs().Resolve(bug1.Id())
+ require.NoError(t, err)
+ _, err = cache.Bugs().ResolveExcerpt(bug1.Id())
+ require.NoError(t, err)
+ _, err = cache.Bugs().ResolvePrefix(bug1.Id().String()[:10])
+ require.NoError(t, err)
- // Reload, only excerpt are loaded, but as we need to load the identities used in the bugs
- // to check the signatures, we also load the identity used above
- cache, err = NewRepoCacheNoEvents(repo)
- require.NoError(t, err)
+ // Querying
+ q, err := query.Parse("status:open author:descartes sort:edit-asc")
+ require.NoError(t, err)
+ res, err := cache.Bugs().Query(q)
+ require.NoError(t, err)
+ require.Len(t, res, 2)
- require.Len(t, cache.bugs.cached, 0)
- require.Len(t, cache.bugs.excerpts, 2)
- require.Len(t, cache.identities.cached, 0)
- require.Len(t, cache.identities.excerpts, 2)
- require.Equal(t, uint64(2), indexCount(t, identity.Namespace))
- require.Equal(t, uint64(2), indexCount(t, bug.Namespace))
+ q, err = query.Parse("status:open marker") // full-text search
+ require.NoError(t, err)
+ res, err = cache.Bugs().Query(q)
+ require.NoError(t, err)
+ require.Len(t, res, 1)
- // Resolving load from the disk
- _, err = cache.Identities().Resolve(iden1.Id())
- require.NoError(t, err)
- _, err = cache.Identities().ResolveExcerpt(iden1.Id())
- require.NoError(t, err)
- _, err = cache.Identities().ResolvePrefix(iden1.Id().String()[:10])
- require.NoError(t, err)
+ // Close
+ require.NoError(t, cache.Close())
+ require.Empty(t, cache.bugs.cached)
+ require.Empty(t, cache.bugs.excerpts)
+ require.Empty(t, cache.identities.cached)
+ require.Empty(t, cache.identities.excerpts)
+
+ // Reload, only excerpt are loaded, but as we need to load the identities used in the bugs
+ // to check the signatures, we also load the identity used above
+ cache, err = NewRepoCacheNoEvents(repo)
+ require.NoError(t, err)
- _, err = cache.Bugs().Resolve(bug1.Id())
- require.NoError(t, err)
- _, err = cache.Bugs().ResolveExcerpt(bug1.Id())
- require.NoError(t, err)
- _, err = cache.Bugs().ResolvePrefix(bug1.Id().String()[:10])
- require.NoError(t, err)
+ require.Len(t, cache.bugs.cached, 0)
+ require.Len(t, cache.bugs.excerpts, 2)
+ require.Len(t, cache.identities.cached, 0)
+ require.Len(t, cache.identities.excerpts, 2)
+ require.Equal(t, uint64(2), indexCount(t, identity.Namespace))
+ require.Equal(t, uint64(2), indexCount(t, bug.Namespace))
- require.Len(t, cache.bugs.cached, 1)
- require.Len(t, cache.bugs.excerpts, 2)
- require.Len(t, cache.identities.cached, 1)
- require.Len(t, cache.identities.excerpts, 2)
- require.Equal(t, uint64(2), indexCount(t, identity.Namespace))
- require.Equal(t, uint64(2), indexCount(t, bug.Namespace))
+ // Resolving load from the disk
+ _, err = cache.Identities().Resolve(iden1.Id())
+ require.NoError(t, err)
+ _, err = cache.Identities().ResolveExcerpt(iden1.Id())
+ require.NoError(t, err)
+ _, err = cache.Identities().ResolvePrefix(iden1.Id().String()[:10])
+ require.NoError(t, err)
- // Remove + RemoveAll
- err = cache.Identities().Remove(iden1.Id().String()[:10])
- require.NoError(t, err)
- err = cache.Bugs().Remove(bug1.Id().String()[:10])
- require.NoError(t, err)
- require.Len(t, cache.bugs.cached, 0)
- require.Len(t, cache.bugs.excerpts, 1)
- require.Len(t, cache.identities.cached, 0)
- require.Len(t, cache.identities.excerpts, 1)
- require.Equal(t, uint64(1), indexCount(t, identity.Namespace))
- require.Equal(t, uint64(1), indexCount(t, bug.Namespace))
+ _, err = cache.Bugs().Resolve(bug1.Id())
+ require.NoError(t, err)
+ _, err = cache.Bugs().ResolveExcerpt(bug1.Id())
+ require.NoError(t, err)
+ _, err = cache.Bugs().ResolvePrefix(bug1.Id().String()[:10])
+ require.NoError(t, err)
- _, err = cache.Identities().New("René Descartes", "rene@descartes.fr")
- require.NoError(t, err)
- _, _, err = cache.Bugs().NewRaw(iden2, time.Now().Unix(), "title", "message", nil, nil)
- require.NoError(t, err)
+ require.Len(t, cache.bugs.cached, 1)
+ require.Len(t, cache.bugs.excerpts, 2)
+ require.Len(t, cache.identities.cached, 1)
+ require.Len(t, cache.identities.excerpts, 2)
+ require.Equal(t, uint64(2), indexCount(t, identity.Namespace))
+ require.Equal(t, uint64(2), indexCount(t, bug.Namespace))
- err = cache.RemoveAll()
- require.NoError(t, err)
- require.Len(t, cache.bugs.cached, 0)
- require.Len(t, cache.bugs.excerpts, 0)
- require.Len(t, cache.identities.cached, 0)
- require.Len(t, cache.identities.excerpts, 0)
- require.Equal(t, uint64(0), indexCount(t, identity.Namespace))
- require.Equal(t, uint64(0), indexCount(t, bug.Namespace))
+ // Remove + RemoveAll
+ err = cache.Identities().Remove(iden1.Id().String()[:10])
+ require.NoError(t, err)
+ err = cache.Bugs().Remove(bug1.Id().String()[:10])
+ require.NoError(t, err)
+ require.Len(t, cache.bugs.cached, 0)
+ require.Len(t, cache.bugs.excerpts, 1)
+ require.Len(t, cache.identities.cached, 0)
+ require.Len(t, cache.identities.excerpts, 1)
+ require.Equal(t, uint64(1), indexCount(t, identity.Namespace))
+ require.Equal(t, uint64(1), indexCount(t, bug.Namespace))
+
+ _, err = cache.Identities().New("René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+ _, _, err = cache.Bugs().NewRaw(iden2, time.Now().Unix(), "title", "message", nil, nil)
+ require.NoError(t, err)
- // Close
- require.NoError(t, cache.Close())
- require.Empty(t, cache.bugs.cached)
- require.Empty(t, cache.bugs.excerpts)
- require.Empty(t, cache.identities.cached)
- require.Empty(t, cache.identities.excerpts)
+ err = cache.RemoveAll()
+ require.NoError(t, err)
+ require.Len(t, cache.bugs.cached, 0)
+ require.Len(t, cache.bugs.excerpts, 0)
+ require.Len(t, cache.identities.cached, 0)
+ require.Len(t, cache.identities.excerpts, 0)
+ require.Equal(t, uint64(0), indexCount(t, identity.Namespace))
+ require.Equal(t, uint64(0), indexCount(t, bug.Namespace))
+
+ // Close
+ require.NoError(t, cache.Close())
+ require.Empty(t, cache.bugs.cached)
+ require.Empty(t, cache.bugs.excerpts)
+ require.Empty(t, cache.identities.cached)
+ require.Empty(t, cache.identities.excerpts)
+ })
}
func TestCachePushPull(t *testing.T) {
diff --git a/misc/completion/bash/git-bug b/misc/completion/bash/git-bug
index 42263e09..49c7c77b 100644
--- a/misc/completion/bash/git-bug
+++ b/misc/completion/bash/git-bug
@@ -352,11 +352,11 @@ _git_bug() {
__git-bug_init_completion -n "=:" || return
fi
- # START PATCH
- # replace in the array ("git","bug", ...) to ("git-bug", ...) and adjust the index in cword
+ # START PATCH
+ # replace in the array ("git","bug", ...) to ("git-bug", ...) and adjust the index in cword
words=("git-bug" "${words[@]:2}")
cword=$(($cword-1))
- # END PATCH
+ # END PATCH
__git-bug_debug
__git-bug_debug "========= starting completion logic =========="
diff --git a/misc/completion/generate.go b/misc/completion/generate.go
index 245c561c..b64c1034 100644
--- a/misc/completion/generate.go
+++ b/misc/completion/generate.go
@@ -66,11 +66,11 @@ _git_bug() {
__git-bug_init_completion -n "=:" || return
fi
- # START PATCH
- # replace in the array ("git","bug", ...) to ("git-bug", ...) and adjust the index in cword
+ # START PATCH
+ # replace in the array ("git","bug", ...) to ("git-bug", ...) and adjust the index in cword
words=("git-bug" "${words[@]:2}")
cword=$(($cword-1))
- # END PATCH
+ # END PATCH
__git-bug_debug
__git-bug_debug "========= starting completion logic =========="
diff --git a/nix/checks/pinact.nix b/nix/checks/pinact.nix
index c032bc9f..109bd14f 100644
--- a/nix/checks/pinact.nix
+++ b/nix/checks/pinact.nix
@@ -1,7 +1,12 @@
{ pkgs, src }:
-pkgs.writeShellApplication {
- name = "pinact";
- runtimeInputs = with pkgs; [ pinact ];
- text = "pinact run --check --verify";
-}
+pkgs.runCommand "pinact"
+ {
+ inherit src;
+ nativeBuildInputs = with pkgs; [ pinact ];
+ }
+ ''
+ cd "$src"
+ pinact run --check --verify
+ touch "$out"
+ ''