Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/workflow-approval.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ jobs:
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
id: app-token
with:
app-id: ${{ secrets.DEVSY_GITHUB_APP_ID }}
client-id: ${{ secrets.DEVSY_GITHUB_APP_ID }}
private-key: ${{ secrets.DEVSY_GITHUB_APP_PRIVATE_KEY }}

- uses: skevetter/automatic-approve-action@80f16f6e6bea1dfc0f613df222390ee143a4d8e9 # v2
with:
token: ${{ steps.app-token.outputs.token }}
workflows: "commit.yml,lint.yml,pr-ci.yml,pre-commit.yml,release-please.yml,promote-release.yml"
workflows: "commit.yml,pr-ci.yml,pre-commit.yml"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

for workflow in commit.yml pr-ci.yml pre-commit.yml; do
  test -f ".github/workflows/$workflow" || {
    echo "Missing .github/workflows/$workflow"
    exit 1
  }
done

Repository: devsy-org/devsy

Length of output: 194


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "Workflow file list containing pre-commit or pr-ci:"
git ls-files '.github/workflows/*' | rg '(^|/)(pre-commit|pr-ci)\.ya?ml$' || true

echo
echo "workflow-approval.yml (line 22 area):"
sed -n '1,45p' .github/workflows/workflow-approval.yml 2>/dev/null || true

echo
echo "References to pre-commit jobs/workflows in pr-ci.yml:"
if [ -f .github/workflows/pr-ci.yml ]; then
  nl -ba .github/workflows/pr-ci.yml | sed -n '1,220p'
else
  echo "no .github/workflows/pr-ci.yml"
fi

Repository: devsy-org/devsy

Length of output: 1060


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "Existing workflow files:"
git ls-files '.github/workflows/*' | sed -n '1,120p'

echo
echo ".github/workflows/pr-ci.yml:"
awk '{printf "%6d    %s\n", NR, $0}' .github/workflows/pr-ci.yml | sed -n '1,220p'

echo
echo "Search for pre-commit references:"
rg -n "pre-commit|pre_commit|pr-ci|commit\.yml" .github/workflows -S || true

Repository: devsy-org/devsy

Length of output: 9681


Remove the missing pre-commit.yml workflow reference.

.github/workflows/pre-commit.yml is not tracked, while the pre-commit task is already a job in .github/workflows/pr-ci.yml. Keeping this allowlisted path can make automatic-approve fail and leave PR runs unapproved.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/workflow-approval.yml at line 22, Remove the missing
pre-commit.yml entry from the workflows allowlist in the workflow-approval
configuration, leaving only the tracked workflow references including pr-ci.yml.

21 changes: 18 additions & 3 deletions cmd/workspace/up/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,23 @@ func (cmd *UpCmd) openIDE(
}

ideConfig := client.WorkspaceConfig().IDE
return opener.Open(ctx, ideConfig.Name, ideConfig.Options, opener.IDEParams{
GPGAgentForwarding: cmd.GPGAgentForwarding,
return opener.Open(ctx, ideConfig.Name, ideConfig.Options, cmd.buildIDEParams(
devsyConfig,
client,
wctx,
))
}

func (cmd *UpCmd) buildIDEParams(
devsyConfig *config.Config,
client client2.BaseWorkspaceClient,
wctx *workspaceContext,
) opener.IDEParams {
setupGPGAgentForwarding := cmd.GPGAgentForwarding ||
devsyConfig.ContextOptionBool(config.ContextOptionGPGAgentForwarding)

return opener.IDEParams{
GPGAgentForwarding: setupGPGAgentForwarding,
SSHAuthSockID: cmd.SSHAuthSockID,
GitSSHSigningKey: cmd.GitSSHSigningKey,
DevsyConfig: devsyConfig,
Expand All @@ -87,7 +102,7 @@ func (cmd *UpCmd) openIDE(
Result: wctx.result,
TunnelMode: wctx.tunnelPort > 0,
Launch: cmd.IDELaunch,
})
}
}

func (cmd *UpCmd) reconfigureSSHWithTunnel(
Expand Down
86 changes: 86 additions & 0 deletions cmd/workspace/up/configure_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package up

import (
"testing"

"github.com/devsy-org/devsy/cmd/flags"
"github.com/devsy-org/devsy/pkg/config"
config2 "github.com/devsy-org/devsy/pkg/devcontainer/config"
"github.com/devsy-org/devsy/pkg/ide/opener"
provider2 "github.com/devsy-org/devsy/pkg/provider"
"github.com/stretchr/testify/assert"
)

const vscodeUser = "vscode"

func TestBuildIDEParams_GPGAgentForwardingParity(t *testing.T) {
tests := []struct {
name string
cmdFlag bool
contextOption string
expected bool
}{
{
name: "enabled by context option only",
cmdFlag: false,
contextOption: config.BoolTrue,
expected: true,
},
{
name: "enabled by cli flag only",
cmdFlag: true,
contextOption: config.BoolFalse,
expected: true,
},
{
name: "disabled when both are off",
cmdFlag: false,
contextOption: config.BoolFalse,
expected: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := &UpCmd{
GlobalFlags: &flags.GlobalFlags{},
CLIOptions: provider2.CLIOptions{
SSHAuthSockID: "sock-id",
GitSSHSigningKey: "ssh-sign-key",
},
GPGAgentForwarding: tt.cmdFlag,
IDELaunch: opener.LaunchAuto,
}

devsyConfig := testConfigWithGPGForwardingOption(tt.contextOption)
wctx := &workspaceContext{
user: vscodeUser,
result: &config2.Result{},
tunnelPort: 10800,
}

params := cmd.buildIDEParams(devsyConfig, nil, wctx)

assert.Equal(t, tt.expected, params.GPGAgentForwarding)
assert.Equal(t, "sock-id", params.SSHAuthSockID)
assert.Equal(t, "ssh-sign-key", params.GitSSHSigningKey)
assert.Equal(t, vscodeUser, params.User)
assert.Equal(t, wctx.result, params.Result)
assert.True(t, params.TunnelMode)
assert.Equal(t, opener.LaunchAuto, params.Launch)
})
}
}

func testConfigWithGPGForwardingOption(value string) *config.Config {
return &config.Config{
DefaultContext: config.DefaultContext,
Contexts: map[string]*config.ContextConfig{
config.DefaultContext: {
Options: map[string]config.OptionValue{
config.ContextOptionGPGAgentForwarding: {Value: value},
},
},
},
}
}
87 changes: 87 additions & 0 deletions e2e/tests/ide/browser_returns.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"time"

"github.com/devsy-org/devsy/e2e/framework"
"github.com/devsy-org/devsy/pkg/config"
"github.com/devsy-org/devsy/pkg/flags/names"
"github.com/devsy-org/devsy/pkg/ide/opener"
"github.com/onsi/ginkgo/v2"
Expand Down Expand Up @@ -365,6 +366,92 @@ var _ = ginkgo.Describe(
},
)

ginkgo.It(
"forwards GPG in browser IDE when enabled only via context option",
ginkgo.Label("gpg"),
ginkgo.SpecTimeout(framework.TimeoutLong()),
func(ctx context.Context) {
f := framework.NewDefaultFramework(initialDir + "/bin")
tempDir, err := framework.CopyToTempDir("tests/ide/testdata-gpg-nonroot")
framework.ExpectNoError(err)
ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir)

contextName := fmt.Sprintf("gpg-ctx-%d", time.Now().UnixNano())
err = f.DevsyContextCreate(ctx, contextName)
framework.ExpectNoError(err)
ginkgo.DeferCleanup(func(cleanupCtx context.Context) {
_ = f.DevsyContextUse(cleanupCtx, config.DefaultContext)
_ = f.DevsyContextDelete(cleanupCtx, contextName)
})

err = f.DevsyContextUse(ctx, contextName)
framework.ExpectNoError(err)
err = f.ExecCommand(
ctx,
false,
true,
"",
[]string{
"context",
"set",
names.Flag(names.Option),
config.ContextOptionGPGAgentForwarding + "=" + config.BoolTrue,
},
)
framework.ExpectNoError(err)

err = f.DevsyProviderAdd(ctx, "docker")
framework.ExpectNoError(err)
err = f.DevsyProviderUse(ctx, "docker")
framework.ExpectNoError(err)
ginkgo.DeferCleanup(func(cleanupCtx context.Context) {
_ = f.DevsyWorkspaceDelete(cleanupCtx, tempDir)
})

ginkgo.GinkgoT().Setenv("GNUPGHOME", ginkgo.GinkgoT().TempDir())
framework.ExpectNoError(
framework.ImportGpgKey(
filepath.Join(
initialDir,
"tests/ssh/testdata/gpg-forwarding/gpg-private.key",
),
),
)

stdout, stderr, err := f.DevsyUpStreamsRaw(ctx, tempDir,
"--ide=openvscode", "--ide-launch=headless", "--debug")
framework.ExpectNoError(err)
combined := stdout + stderr
gomega.Expect(combined).
To(gomega.ContainSubstring("Starting vscode in browser mode at"),
"expected browser IDE opener path to execute; got:\n%s", combined)
gomega.Expect(combined).To(gomega.ContainSubstring("forwarding gpg-agent"),
"expected browser IDE GPG forward bootstrap to run; got:\n%s", combined)

gomega.Expect(combined).NotTo(gomega.ContainSubstring("continuing without it"),
"GPG agent forwarding must succeed when enabled from context option only; got:\n%s", combined)
gomega.Expect(combined).NotTo(gomega.ContainSubstring(
"timed out waiting for gpg-agent forward to become ready",
), "GPG forward should be ready before handing off browser IDE session; got:\n%s", combined)

ws, err := f.FindWorkspace(ctx, tempDir)
framework.ExpectNoError(err)
state, err := opener.ReadTunnelState(ws.Context, ws.ID)
framework.ExpectNoError(err)
gomega.Expect(state).NotTo(gomega.BeNil(),
"expected browser tunnel state to exist for IDE session")
gomega.Expect(state.PID).To(gomega.BeNumerically(">", 0),
"expected detached browser tunnel process to be running")

sshCtx, cancelSSH := context.WithDeadline(ctx, time.Now().Add(30*time.Second))
defer cancelSSH()
err = f.DevsySSHGpgSecretKeyForwarded(sshCtx, tempDir, gpgTestKeyFingerprint)
framework.ExpectNoError(err)

framework.ExpectNoError(f.DevsyStop(ctx, tempDir))
},
)

ginkgo.It(
"does not log 'setup KubeConfig' on a workspace without kubeconfig forwarding",
ginkgo.SpecTimeout(framework.TimeoutLong()),
Expand Down
Loading