From 3e50e0897358599db2b535af6b3eab55c8e84ff9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 01:12:00 +0000 Subject: [PATCH 1/4] Initial plan From 25164fd62f7489468ecf627314b7076528ffbb27 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 01:17:58 +0000 Subject: [PATCH 2/4] ci: add automated test workflow and manual test workflow Agent-Logs-Url: https://github.com/ohto-ai/webssh_cpp/sessions/a4b80580-46e6-4c54-a50e-31fb0e45fd9c Co-authored-by: ohto-ai <46275725+ohto-ai@users.noreply.github.com> --- .github/workflows/manual_test.yaml | 106 +++++++++++++++++++++++++++++ .github/workflows/test.yaml | 75 ++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 .github/workflows/manual_test.yaml create mode 100644 .github/workflows/test.yaml diff --git a/.github/workflows/manual_test.yaml b/.github/workflows/manual_test.yaml new file mode 100644 index 0000000..47fe4b5 --- /dev/null +++ b/.github/workflows/manual_test.yaml @@ -0,0 +1,106 @@ +name: Manual Test + +on: + workflow_dispatch: + inputs: + duration: + type: string + description: 'How long to keep the test environment running (minutes)' + required: false + default: '30' + +jobs: + manual-test: + name: Manual Test Environment + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: 'true' + fetch-depth: 0 + + - name: Set up SSH server and test account + id: setup + shell: bash + run: | + sudo apt-get install -y openssh-server + # Enable password authentication + sudo sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config + sudo systemctl start ssh + # Generate a random test user and password + TEST_USER=webssh_tester + TEST_PASS=$(openssl rand -base64 16 | tr -dc 'A-Za-z0-9' | head -c 20) + sudo useradd -m -s /bin/bash "$TEST_USER" + echo "$TEST_USER:$TEST_PASS" | sudo chpasswd + echo "test_user=$TEST_USER" >> "$GITHUB_OUTPUT" + echo "test_pass=$TEST_PASS" >> "$GITHUB_OUTPUT" + + - name: Configure and build + shell: bash + run: | + cmake -Bbuild -DCMAKE_BUILD_TYPE=Release + cmake --build build --target webssh_cpp -j4 + + - name: Start webssh_cpp + shell: bash + run: | + ./build/src/webssh_cpp -p 8080 & + echo "APP_PID=$!" >> "$GITHUB_ENV" + sleep 2 + echo "webssh_cpp started on port 8080" + + - name: Set up public tunnel + shell: bash + run: | + # Start a reverse SSH tunnel via localhost.run (no account required) + ssh -o StrictHostKeyChecking=no \ + -o ServerAliveInterval=30 \ + -o ServerAliveCountMax=10 \ + -R 80:localhost:8080 \ + nokey@localhost.run 2>&1 | tee /tmp/tunnel.log & + echo "TUNNEL_PID=$!" >> "$GITHUB_ENV" + # Wait for the tunnel URL to appear in the log + for i in $(seq 1 15); do + TUNNEL_URL=$(grep -oP 'https?://[^\s]+' /tmp/tunnel.log | head -1) + if [ -n "$TUNNEL_URL" ]; then + break + fi + sleep 2 + done + echo "TUNNEL_URL=${TUNNEL_URL:-http://localhost:8080 (tunnel unavailable)}" >> "$GITHUB_ENV" + + - name: Print test environment info + shell: bash + run: | + echo "============================================" + echo " WebSSH Manual Test Environment" + echo "============================================" + echo "" + echo " 1. Open the web interface in your browser:" + echo " $TUNNEL_URL" + echo "" + echo " 2. In the web terminal, connect with these SSH credentials:" + echo " Host : 127.0.0.1" + echo " Port : 22" + echo " Username : ${{ steps.setup.outputs.test_user }}" + echo " Password : ${{ steps.setup.outputs.test_pass }}" + echo "" + echo " Environment will stay up for ${{ inputs.duration }} minutes." + echo " Cancel this workflow run to shut it down early." + echo "============================================" + + - name: Wait for manual testing + shell: bash + run: | + DURATION_INPUT="${{ inputs.duration }}" + # Validate that the input is a positive integer; fall back to 30 if not. + if ! [[ "$DURATION_INPUT" =~ ^[0-9]+$ ]] || [ "$DURATION_INPUT" -le 0 ]; then + DURATION_INPUT=30 + fi + DURATION_SECONDS=$(( DURATION_INPUT * 60 )) + echo "Keeping environment alive for ${DURATION_INPUT} minute(s)..." + sleep "$DURATION_SECONDS" diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..1f1a317 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,75 @@ +name: Test + +on: + push: + paths: + - '.github/workflows/test.yaml' + - 'src/**' + - 'inc/**' + - 'test/**' + - 'CMakeLists.txt' + - '*.cmake' + pull_request: + paths: + - '.github/workflows/test.yaml' + - 'src/**' + - 'inc/**' + - 'test/**' + - 'CMakeLists.txt' + - '*.cmake' + +jobs: + test: + name: ${{ matrix.os }} - test + runs-on: ${{ matrix.os }} + permissions: + contents: read + strategy: + matrix: + os: [ubuntu-22.04, ubuntu-latest] + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: 'true' + fetch-depth: 0 + + - name: Set up SSH server for integration tests + shell: bash + run: | + sudo apt-get install -y openssh-server + # Enable password authentication + sudo sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config + sudo systemctl start ssh + # Create a temporary test user with a random password + TEST_USER=webssh_testuser + TEST_PASS=$(openssl rand -base64 12 | tr -dc 'A-Za-z0-9' | head -c 16) + sudo useradd -m -s /bin/bash "$TEST_USER" + echo "$TEST_USER:$TEST_PASS" | sudo chpasswd + # Export credentials for subsequent steps + echo "TEST_SSH_HOST=127.0.0.1" >> "$GITHUB_ENV" + echo "TEST_SSH_PORT=22" >> "$GITHUB_ENV" + echo "TEST_SSH_USER=$TEST_USER" >> "$GITHUB_ENV" + echo "TEST_SSH_PASS=$TEST_PASS" >> "$GITHUB_ENV" + + - name: Set reusable strings + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + + - name: Configure + shell: bash + run: | + cmake -B${{ steps.strings.outputs.build-output-dir }} -DCMAKE_BUILD_TYPE=Debug + + - name: Build tests + shell: bash + run: | + cmake --build ${{ steps.strings.outputs.build-output-dir }} --target webssh_cpp_tests -j4 + + - name: Run tests + shell: bash + run: | + ctest --test-dir ${{ steps.strings.outputs.build-output-dir }} --output-on-failure From aa58e620e9755cd1cfc05e63393df09a828784a4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 05:47:39 +0000 Subject: [PATCH 3/4] ci: apply review feedback and add windows test support Agent-Logs-Url: https://github.com/ohto-ai/webssh_cpp/sessions/0c9f942a-3177-4e23-ace0-3aa373be5981 Co-authored-by: ohto-ai <46275725+ohto-ai@users.noreply.github.com> --- .github/workflows/manual_test.yaml | 56 +++++++++++++----------------- .github/workflows/test.yaml | 49 +++++++++++++++----------- 2 files changed, 54 insertions(+), 51 deletions(-) diff --git a/.github/workflows/manual_test.yaml b/.github/workflows/manual_test.yaml index 47fe4b5..efded11 100644 --- a/.github/workflows/manual_test.yaml +++ b/.github/workflows/manual_test.yaml @@ -5,7 +5,7 @@ on: inputs: duration: type: string - description: 'How long to keep the test environment running (minutes)' + description: 'How long to keep the test environment running, in minutes (max 60)' required: false default: '30' @@ -15,6 +15,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + timeout-minutes: 65 steps: - name: Checkout @@ -27,13 +28,14 @@ jobs: id: setup shell: bash run: | + sudo apt-get update sudo apt-get install -y openssh-server # Enable password authentication sudo sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config sudo systemctl start ssh # Generate a random test user and password TEST_USER=webssh_tester - TEST_PASS=$(openssl rand -base64 16 | tr -dc 'A-Za-z0-9' | head -c 20) + TEST_PASS=$(openssl rand -hex 10) sudo useradd -m -s /bin/bash "$TEST_USER" echo "$TEST_USER:$TEST_PASS" | sudo chpasswd echo "test_user=$TEST_USER" >> "$GITHUB_OUTPUT" @@ -49,30 +51,9 @@ jobs: shell: bash run: | ./build/src/webssh_cpp -p 8080 & - echo "APP_PID=$!" >> "$GITHUB_ENV" sleep 2 echo "webssh_cpp started on port 8080" - - name: Set up public tunnel - shell: bash - run: | - # Start a reverse SSH tunnel via localhost.run (no account required) - ssh -o StrictHostKeyChecking=no \ - -o ServerAliveInterval=30 \ - -o ServerAliveCountMax=10 \ - -R 80:localhost:8080 \ - nokey@localhost.run 2>&1 | tee /tmp/tunnel.log & - echo "TUNNEL_PID=$!" >> "$GITHUB_ENV" - # Wait for the tunnel URL to appear in the log - for i in $(seq 1 15); do - TUNNEL_URL=$(grep -oP 'https?://[^\s]+' /tmp/tunnel.log | head -1) - if [ -n "$TUNNEL_URL" ]; then - break - fi - sleep 2 - done - echo "TUNNEL_URL=${TUNNEL_URL:-http://localhost:8080 (tunnel unavailable)}" >> "$GITHUB_ENV" - - name: Print test environment info shell: bash run: | @@ -80,19 +61,28 @@ jobs: echo " WebSSH Manual Test Environment" echo "============================================" echo "" - echo " 1. Open the web interface in your browser:" - echo " $TUNNEL_URL" + echo " webssh_cpp is running on port 8080 of this runner." + echo " Connect to the tmate session shown below, then" + echo " forward port 8080 to your local machine:" + echo " ssh -L 8080:localhost:8080 " + echo " Then open http://localhost:8080/ in your browser." echo "" - echo " 2. In the web terminal, connect with these SSH credentials:" - echo " Host : 127.0.0.1" - echo " Port : 22" - echo " Username : ${{ steps.setup.outputs.test_user }}" - echo " Password : ${{ steps.setup.outputs.test_pass }}" + echo " SSH credentials to enter in the web terminal:" + echo " Host : 127.0.0.1" + echo " Port : 22" + echo " Username : ${{ steps.setup.outputs.test_user }}" + echo " Password : ${{ steps.setup.outputs.test_pass }}" echo "" - echo " Environment will stay up for ${{ inputs.duration }} minutes." + echo " Environment will stay up for ${{ inputs.duration }} minutes (max 60)." echo " Cancel this workflow run to shut it down early." echo "============================================" + - name: Set up tmate session + uses: mxschmitt/action-tmate@v3 + with: + limit-access-to-actor: true + detached: true + - name: Wait for manual testing shell: bash run: | @@ -101,6 +91,10 @@ jobs: if ! [[ "$DURATION_INPUT" =~ ^[0-9]+$ ]] || [ "$DURATION_INPUT" -le 0 ]; then DURATION_INPUT=30 fi + # Cap at 60 minutes to avoid excessive runner usage. + if [ "$DURATION_INPUT" -gt 60 ]; then + DURATION_INPUT=60 + fi DURATION_SECONDS=$(( DURATION_INPUT * 60 )) echo "Keeping environment alive for ${DURATION_INPUT} minute(s)..." sleep "$DURATION_SECONDS" diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 1f1a317..713f741 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,22 +1,7 @@ name: Test on: - push: - paths: - - '.github/workflows/test.yaml' - - 'src/**' - - 'inc/**' - - 'test/**' - - 'CMakeLists.txt' - - '*.cmake' - pull_request: - paths: - - '.github/workflows/test.yaml' - - 'src/**' - - 'inc/**' - - 'test/**' - - 'CMakeLists.txt' - - '*.cmake' + workflow_dispatch: jobs: test: @@ -24,9 +9,10 @@ jobs: runs-on: ${{ matrix.os }} permissions: contents: read + timeout-minutes: 60 strategy: matrix: - os: [ubuntu-22.04, ubuntu-latest] + os: [ubuntu-22.04, ubuntu-latest, windows-latest] steps: - name: Checkout @@ -35,16 +21,18 @@ jobs: submodules: 'true' fetch-depth: 0 - - name: Set up SSH server for integration tests + - name: Set up SSH server for integration tests (Linux) + if: runner.os == 'Linux' shell: bash run: | + sudo apt-get update sudo apt-get install -y openssh-server # Enable password authentication sudo sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config sudo systemctl start ssh # Create a temporary test user with a random password TEST_USER=webssh_testuser - TEST_PASS=$(openssl rand -base64 12 | tr -dc 'A-Za-z0-9' | head -c 16) + TEST_PASS=$(openssl rand -hex 8) sudo useradd -m -s /bin/bash "$TEST_USER" echo "$TEST_USER:$TEST_PASS" | sudo chpasswd # Export credentials for subsequent steps @@ -53,11 +41,32 @@ jobs: echo "TEST_SSH_USER=$TEST_USER" >> "$GITHUB_ENV" echo "TEST_SSH_PASS=$TEST_PASS" >> "$GITHUB_ENV" + - name: Set up SSH server for integration tests (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + # Install and start the built-in OpenSSH server + Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 + Start-Service sshd + # Create a temporary test user with a cryptographically random password + $TestUser = "webssh_testuser" + $bytes = [byte[]]::new(8) + [System.Security.Cryptography.RandomNumberGenerator]::Fill($bytes) + $TestPass = [System.Convert]::ToHexString($bytes).ToLower() + $SecPass = ConvertTo-SecureString $TestPass -AsPlainText -Force + New-LocalUser $TestUser -Password $SecPass -PasswordNeverExpires + # Export credentials for subsequent steps + "TEST_SSH_HOST=127.0.0.1" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + "TEST_SSH_PORT=22" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + "TEST_SSH_USER=$TestUser" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + "TEST_SSH_PASS=$TestPass" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + - name: Set reusable strings id: strings shell: bash run: | - echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + unix_workspace_path=$(echo "${{ github.workspace }}" | sed 's/\\/\//g') + echo "build-output-dir=${unix_workspace_path}/build" >> "$GITHUB_OUTPUT" - name: Configure shell: bash From 313dade4ea46bb60d71ce87a853b3cc867b3fe1d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 10:05:04 +0000 Subject: [PATCH 4/4] ci: add password input to manual test workflow Agent-Logs-Url: https://github.com/ohto-ai/webssh_cpp/sessions/af1abb93-13c9-47ce-a70c-68c2f00d17d2 Co-authored-by: ohto-ai <46275725+ohto-ai@users.noreply.github.com> --- .github/workflows/manual_test.yaml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/manual_test.yaml b/.github/workflows/manual_test.yaml index efded11..56822db 100644 --- a/.github/workflows/manual_test.yaml +++ b/.github/workflows/manual_test.yaml @@ -3,6 +3,11 @@ name: Manual Test on: workflow_dispatch: inputs: + password: + type: string + description: 'Password for the test SSH account (webssh_tester). Leave blank to auto-generate a random password.' + required: false + default: '' duration: type: string description: 'How long to keep the test environment running, in minutes (max 60)' @@ -27,15 +32,21 @@ jobs: - name: Set up SSH server and test account id: setup shell: bash + env: + TEST_PASS_INPUT: ${{ inputs.password }} run: | sudo apt-get update sudo apt-get install -y openssh-server # Enable password authentication sudo sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config sudo systemctl start ssh - # Generate a random test user and password + # Use the provided password or auto-generate one TEST_USER=webssh_tester - TEST_PASS=$(openssl rand -hex 10) + if [ -n "$TEST_PASS_INPUT" ]; then + TEST_PASS="$TEST_PASS_INPUT" + else + TEST_PASS=$(openssl rand -hex 10) + fi sudo useradd -m -s /bin/bash "$TEST_USER" echo "$TEST_USER:$TEST_PASS" | sudo chpasswd echo "test_user=$TEST_USER" >> "$GITHUB_OUTPUT"