diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d0eebc6fc..3784f3444 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -24,7 +24,7 @@ jobs: fail-fast: false matrix: runner-os: [windows-latest, ubuntu-latest, macos-latest] - language: [ csharp, actions ] + language: [csharp, actions] runs-on: ${{ matrix.runner-os }} @@ -32,6 +32,9 @@ jobs: - uses: actions/checkout@v6 with: persist-credentials: false + + - uses: extractions/setup-just@f8a3cce218d9f83db3a2ecd90e41ac3de6cdfd9b + # e33e0265a09d6d736e2ee1e0eb685ef1de4669ff is tag v3, pinned to avoid supply chain attacks - uses: extractions/setup-just@f8a3cce218d9f83db3a2ecd90e41ac3de6cdfd9b - name: Initialize CodeQL @@ -64,7 +67,6 @@ jobs: run: cp coverage/**/coverage.cobertura.xml coverage/coverage.cobertura.xml - name: Code Coverage Summary Report - # 51cc3a756ddcd398d447c044c02cb6aa83fdae95 is tag v1.3.0, pinned to avoid supply chain attacks uses: irongut/CodeCoverageSummary@51cc3a756ddcd398d447c044c02cb6aa83fdae95 if: always() && matrix.runner-os == 'ubuntu-latest' && matrix.language == 'csharp' with: @@ -73,18 +75,16 @@ jobs: format: "markdown" output: "both" - # This is used by the subsequent publish-test-results.yml - name: Upload Unit Test Results if: always() && matrix.runner-os == 'ubuntu-latest' && matrix.language == 'csharp' - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: Unit Test Results path: src/OctoshiftCLI.Tests/unit-tests.xml - # This is used by the subsequent publish-test-results.yml - name: Upload Code Coverage Report if: always() && matrix.runner-os == 'ubuntu-latest' && matrix.language == 'csharp' - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: Code Coverage Report path: code-coverage-results.md @@ -93,28 +93,7 @@ jobs: uses: github/codeql-action/analyze@v4 if: matrix.runner-os == 'ubuntu-latest' - upload-event-file: - permissions: - contents: read - actions: write - runs-on: ubuntu-latest - steps: - # This is used by the subsequent publish-test-results.yaml - - name: Upload Event File - uses: actions/upload-artifact@v6 - with: - name: Event File - path: ${{ github.event_path }} - build-for-e2e-test: - permissions: - contents: read - actions: write - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login == 'github' - strategy: - fail-fast: false - matrix: - target-os: [windows-latest, ubuntu-latest, macos-latest] runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..34a44a7f9 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,101 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + - cron: '45 23 * * 5' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: csharp + build-mode: none + # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Add any setup steps before running the `github/codeql-action/init` action. + # This includes steps like installing compilers or runtimes (`actions/setup-node` + # or others). This is typically only required for manual builds. + # - name: Setup runtime (example) + # uses: actions/setup-example@v1 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - name: Run manual build steps + if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index ddfe68ef8..abbb44365 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/checkout@v6 - name: Setup .NET - uses: actions/setup-dotnet@v2 + uses: actions/setup-dotnet@v5 with: global-json-file: global.json diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 000000000..fb2249f86 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,98 @@ +name: Docker + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +on: + schedule: + - cron: '36 13 * * *' + push: + branches: [ "main" ] + # Publish semver tags as releases. + tags: [ 'v*.*.*' ] + pull_request: + branches: [ "main" ] + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: ${{ github.repository }} + + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Install the cosign tool except on PR + # https://github.com/sigstore/cosign-installer + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad #v4.0.0 + with: + cosign-release: 'v2.2.4' + + # Set up BuildKit Docker container builder to be able to build + # multi-platform images and export cache + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # Sign the resulting Docker image digest except on PRs. + # This will only write to the public Rekor transparency log when the Docker + # repository is public to avoid leaking data. If you would like to publish + # transparency data even for private images, pass --force to cosign below. + # https://github.com/sigstore/cosign + - name: Sign the published Docker image + if: ${{ github.event_name != 'pull_request' }} + env: + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable + TAGS: ${{ steps.meta.outputs.tags }} + DIGEST: ${{ steps.build-and-push.outputs.digest }} + # This step uses the identity token to provision an ephemeral certificate + # against the sigstore community Fulcio instance. + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index d5c57f1cf..311ae23ff 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -23,70 +23,70 @@ jobs: target-os: [windows-latest, ubuntu-latest, macos-latest] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 - with: - ref: 'refs/pull/${{ github.event.inputs.pr_number }}/merge' - fetch-depth: 0 + - uses: actions/checkout@v6 + with: + ref: 'refs/pull/${{ github.event.inputs.pr_number }}/merge' + fetch-depth: 0 - - name: Check SHA - run: | - git fetch origin refs/pull/${{ github.event.inputs.pr_number }}/head:pr-head - prsha=`git rev-parse pr-head | awk '{ print $1 }'` + - name: Check SHA + run: | + git fetch origin refs/pull/${{ github.event.inputs.pr_number }}/head:pr-head + prsha=`git rev-parse pr-head | awk '{ print $1 }'` - echo "PR SHA: $prsha" - echo "expected SHA: ${{ github.event.inputs.SHA }}" + echo "PR SHA: $prsha" + echo "expected SHA: ${{ github.event.inputs.sha }}" - if [ "$prsha" != "${{ github.event.inputs.SHA }}" ]; then - echo "SHA must match" >&2 - exit 1 - fi + if [ "$prsha" != "${{ github.event.inputs.sha }}" ]; then + echo "SHA must match" >&2 + exit 1 + fi - - name: Setup .NET - uses: actions/setup-dotnet@v2 - with: - global-json-file: global.json + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + global-json-file: global.json - - name: Build Artifacts (Linux) - if: matrix.target-os == 'ubuntu-latest' - run: ./publish.ps1 - shell: pwsh - env: - SKIP_WINDOWS: "true" - SKIP_MACOS: "true" + - name: Build Artifacts (Linux) + if: matrix.target-os == 'ubuntu-latest' + run: ./publish.ps1 + shell: pwsh + env: + SKIP_WINDOWS: "true" + SKIP_MACOS: "true" - - name: Build Artifacts (Windows) - if: matrix.target-os == 'windows-latest' - run: ./publish.ps1 - shell: pwsh - env: - SKIP_LINUX: "true" - SKIP_MACOS: "true" + - name: Build Artifacts (Windows) + if: matrix.target-os == 'windows-latest' + run: ./publish.ps1 + shell: pwsh + env: + SKIP_LINUX: "true" + SKIP_MACOS: "true" - - name: Build Artifacts (MacOS) - if: matrix.target-os == 'macos-latest' - run: ./publish.ps1 - shell: pwsh - env: - SKIP_WINDOWS: "true" - SKIP_LINUX: "true" + - name: Build Artifacts (MacOS) + if: matrix.target-os == 'macos-latest' + run: ./publish.ps1 + shell: pwsh + env: + SKIP_WINDOWS: "true" + SKIP_LINUX: "true" - - name: Upload Binaries - uses: actions/upload-artifact@v6 - with: - name: binaries-${{ matrix.target-os }} - path: | - dist/linux-x64/ado2gh-linux-amd64 - dist/linux-x64/bbs2gh-linux-amd64 - dist/linux-x64/gei-linux-amd64 - dist/osx-x64/ado2gh-darwin-amd64 - dist/osx-x64/bbs2gh-darwin-amd64 - dist/osx-x64/gei-darwin-amd64 - dist/win-x64/ado2gh-windows-amd64.exe - dist/win-x64/bbs2gh-windows-amd64.exe - dist/win-x64/gei-windows-amd64.exe + - name: Upload Binaries + uses: actions/upload-artifact@v7 + with: + name: binaries-${{ matrix.target-os }} + path: | + dist/linux-x64/ado2gh-linux-amd64 + dist/linux-x64/bbs2gh-linux-amd64 + dist/linux-x64/gei-linux-amd64 + dist/osx-x64/ado2gh-darwin-amd64 + dist/osx-x64/bbs2gh-darwin-amd64 + dist/osx-x64/gei-darwin-amd64 + dist/win-x64/ado2gh-windows-amd64.exe + dist/win-x64/bbs2gh-windows-amd64.exe + dist/win-x64/gei-windows-amd64.exe e2e-test: - needs: [ build-for-e2e-test ] + needs: [build-for-e2e-test] strategy: fail-fast: false matrix: @@ -95,6 +95,139 @@ jobs: runs-on: ${{ matrix.runner-os }} concurrency: integration-test-${{ matrix.source-vcs }}-${{ matrix.runner-os }} steps: + - uses: actions/checkout@v6 + with: + ref: 'refs/pull/${{ github.event.inputs.pr_number }}/merge' + fetch-depth: 0 + + - name: Get PR Commit + if: always() && matrix.runner-os == 'ubuntu-latest' + run: | + prsha=`git ls-remote origin refs/pull/${{ github.event.inputs.pr_number }}/head | awk '{ print $1 }'` + echo "PR_SHA=$prsha" >> $GITHUB_ENV + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + global-json-file: global.json + + - name: Download Binaries + uses: actions/download-artifact@v7 + with: + name: binaries-${{ matrix.runner-os }} + path: dist + + - name: Copy binary to root (linux) + if: matrix.runner-os == 'ubuntu-latest' + run: | + New-Item -Path "./" -Name "gh-gei" -ItemType "directory" + New-Item -Path "./" -Name "gh-ado2gh" -ItemType "directory" + New-Item -Path "./" -Name "gh-bbs2gh" -ItemType "directory" + Copy-Item ./dist/linux-x64/gei-linux-amd64 ./gh-gei/gh-gei + Copy-Item ./dist/linux-x64/ado2gh-linux-amd64 ./gh-ado2gh/gh-ado2gh + Copy-Item ./dist/linux-x64/bbs2gh-linux-amd64 ./gh-bbs2gh/gh-bbs2gh + shell: pwsh + + - name: Copy binary to root (windows) + if: matrix.runner-os == 'windows-latest' + run: | + New-Item -Path "./" -Name "gh-gei" -ItemType "directory" + New-Item -Path "./" -Name "gh-ado2gh" -ItemType "directory" + New-Item -Path "./" -Name "gh-bbs2gh" -ItemType "directory" + Copy-Item ./dist/win-x64/gei-windows-amd64.exe ./gh-gei/gh-gei.exe + Copy-Item ./dist/win-x64/ado2gh-windows-amd64.exe ./gh-ado2gh/gh-ado2gh.exe + Copy-Item ./dist/win-x64/bbs2gh-windows-amd64.exe ./gh-bbs2gh/gh-bbs2gh.exe + shell: pwsh + + - name: Copy binary to root (macos) + if: matrix.runner-os == 'macos-latest' + run: | + New-Item -Path "./" -Name "gh-gei" -ItemType "directory" + New-Item -Path "./" -Name "gh-ado2gh" -ItemType "directory" + New-Item -Path "./" -Name "gh-bbs2gh" -ItemType "directory" + Copy-Item ./dist/osx-x64/gei-darwin-amd64 ./gh-gei/gh-gei + Copy-Item ./dist/osx-x64/ado2gh-darwin-amd64 ./gh-ado2gh/gh-ado2gh + Copy-Item ./dist/osx-x64/bbs2gh-darwin-amd64 ./gh-bbs2gh/gh-bbs2gh + shell: pwsh + + - name: Set execute permissions + run: | + chmod +x ./gh-gei/gh-gei + chmod +x ./gh-ado2gh/gh-ado2gh + chmod +x ./gh-bbs2gh/gh-bbs2gh + + - name: Install gh-gei extension + run: gh extension install . + shell: pwsh + working-directory: ./gh-gei + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Install gh-ado2gh extension + run: gh extension install . + shell: pwsh + working-directory: ./gh-ado2gh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Install gh-bbs2gh extension + run: gh extension install . + shell: pwsh + working-directory: ./gh-bbs2gh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Integration Test + env: + ADO_PAT: ${{ secrets.ADO_PAT }} + GHEC_PAT: ${{ secrets.GHEC_PAT }} + GHES_PAT: ${{ secrets.GHES_PAT }} + ADO_SERVER_PAT: ${{ secrets.ADO_SERVER_PAT }} + BBS_USERNAME: ${{ secrets.BBS_USERNAME }} + BBS_PASSWORD: ${{ secrets.BBS_PASSWORD }} + SSH_KEY_BBS: ${{ secrets.SSH_KEY_BBS }} + SSH_PORT_BBS: ${{ secrets.SSH_PORT_BBS }} + SMB_PASSWORD: ${{ secrets.SMB_PASSWORD }} + AZURE_STORAGE_CONNECTION_STRING_BBS_LINUX: ${{ secrets.AZURE_STORAGE_CONNECTION_STRING_BBS_LINUX }} + AZURE_STORAGE_CONNECTION_STRING_BBS_MACOS: ${{ secrets.AZURE_STORAGE_CONNECTION_STRING_BBS_MACOS }} + AZURE_STORAGE_CONNECTION_STRING_BBS_WINDOWS: ${{ secrets.AZURE_STORAGE_CONNECTION_STRING_BBS_WINDOWS }} + AZURE_STORAGE_CONNECTION_STRING_GHES_LINUX: ${{ secrets.AZURE_STORAGE_CONNECTION_STRING_GHES_LINUX }} + AZURE_STORAGE_CONNECTION_STRING_GHES_MACOS: ${{ secrets.AZURE_STORAGE_CONNECTION_STRING_GHES_MACOS }} + AZURE_STORAGE_CONNECTION_STRING_GHES_WINDOWS: ${{ secrets.AZURE_STORAGE_CONNECTION_STRING_GHES_WINDOWS }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_BUCKET_NAME: ${{ secrets.AWS_BUCKET_NAME }} + GEI_DEBUG_MODE: 'true' + LD_LIBRARY_PATH: "$LD_LIBRARY_PATH:${{ github.workspace }}/src/OctoshiftCLI.IntegrationTests/bin/Debug/net8.0/runtimes/ubuntu.18.04-x64/native" + run: dotnet test src/OctoshiftCLI.IntegrationTests/OctoshiftCLI.IntegrationTests.csproj --filter "${{ matrix.source-vcs }}ToGithub" --logger:"junit;LogFilePath=integration-tests.xml" --logger "console;verbosity=normal" /p:VersionPrefix=9.9 + + - name: Publish Integration Test Results + uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 + if: always() && matrix.runner-os == 'ubuntu-latest' + with: + files: "**/*-tests.xml" + check_name: "Integration Test Results - ${{ matrix.source-vcs }}" + comment_mode: off + commit: ${{ env.PR_SHA }} + + - name: Upload test logs + uses: actions/upload-artifact@v7 + if: always() + with: + name: integration-test-logs-${{ matrix.source-vcs }}-${{ matrix.runner-os }} + path: dist/**/*.log + + - name: Test Logs + if: always() + run: Get-ChildItem . -Filter *.octoshift.log -Recurse | ForEach-Object { Get-Content -Path $_.FullName } + working-directory: ./dist + shell: pwsh + + - name: Test Logs (Verbose) + if: always() + run: Get-ChildItem . -Filter *.octoshift.verbose.log -Recurse | ForEach-Object { Get-Content -Path $_.FullName } + working-directory: ./dist + shell: pwsh - uses: actions/checkout@v6 with: ref: 'refs/pull/${{ github.event.inputs.pr_number }}/merge' diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 000000000..e9d74951b --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,43 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v4 + with: + # Upload entire repository + path: '.' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4