|
1 | 1 | name: pr-check |
2 | 2 |
|
3 | | -# Note: If you need to make changes to this file, please use a branch off the main branch instead of a fork. |
4 | | -# The pull_request target from a forked repo will not have access to the secrets needed for this workflow. |
| 3 | +# Tests PR code against a local SQL Server instance so no Azure credentials are required. |
| 4 | +# This workflow uses the pull_request trigger (not pull_request_target), so fork PRs run |
| 5 | +# with no secrets and no elevated permissions. |
| 6 | +# |
| 7 | +# - Linux runners: spin up SQL Server 2022 in a Docker container with SA auth. |
| 8 | +# - Windows runners: install SQL Server 2025 Express directly from Microsoft with SA auth. |
5 | 9 |
|
6 | 10 | on: |
7 | | - pull_request_target: |
8 | 11 | pull_request: |
9 | | - paths: |
10 | | - - '.github/workflows/pr-check.yml' |
11 | 12 |
|
12 | 13 | permissions: {} |
13 | 14 |
|
14 | 15 | jobs: |
15 | | - # Build job that safely builds artifacts from PR code without access to secrets |
16 | | - build: |
17 | | - environment: Automation test # Require approval before running the action |
| 16 | + test: |
| 17 | + strategy: |
| 18 | + fail-fast: false |
| 19 | + matrix: |
| 20 | + os: [ubuntu-latest, windows-latest] |
18 | 21 | runs-on: ${{ matrix.os }} |
19 | 22 | permissions: |
20 | 23 | contents: read |
21 | | - strategy: |
22 | | - matrix: |
23 | | - os: [windows-latest, ubuntu-latest] |
| 24 | + checks: write |
| 25 | + |
| 26 | + env: |
| 27 | + TEST_DB: SqlActionTest |
| 28 | + |
| 29 | + defaults: |
| 30 | + run: |
| 31 | + shell: bash |
| 32 | + |
24 | 33 | steps: |
25 | | - - name: Checkout from PR branch |
| 34 | + - name: Checkout PR |
26 | 35 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 |
27 | 36 | with: |
28 | | - repository: ${{ github.event.pull_request.head.repo.full_name }} |
29 | 37 | ref: ${{ github.event.pull_request.head.sha }} |
30 | 38 |
|
31 | | - - name: Verify package-lock.json exists |
| 39 | + - name: Generate SA password |
32 | 40 | run: | |
33 | | - if (!(Test-Path package-lock.json)) { |
34 | | - Write-Error "package-lock.json not found. Please commit package-lock.json to ensure reproducible builds." |
35 | | - exit 1 |
36 | | - } |
37 | | - shell: pwsh |
| 41 | + SA_PASSWORD="$(openssl rand -base64 18 | tr -d '/+=')Aa1!" |
| 42 | + echo "::add-mask::${SA_PASSWORD}" |
| 43 | + echo "SA_PASSWORD=${SA_PASSWORD}" >> "$GITHUB_ENV" |
38 | 44 |
|
39 | | - - name: Check if package-lock.json was modified |
40 | | - run: | |
41 | | - # Check git log to see if package-lock.json was modified in this PR |
42 | | - git fetch origin ${{ github.base_ref }} --depth=1 |
43 | | - $changedFiles = git diff --name-only origin/${{ github.base_ref }}...HEAD |
44 | | - |
45 | | - if ($changedFiles -match "package-lock.json") { |
46 | | - Write-Warning "⚠️ package-lock.json has been modified in this PR." |
47 | | - Write-Warning "This requires manual review to ensure no malicious dependencies were added." |
48 | | - Write-Warning "Reviewers: Please carefully examine the dependency changes before approving." |
49 | | - } else { |
50 | | - Write-Host "✓ package-lock.json unchanged - no new dependencies" -ForegroundColor Green |
51 | | - } |
52 | | - shell: pwsh |
53 | | - continue-on-error: true |
54 | | - |
55 | | - - name: Verify package.json integrity |
56 | | - run: | |
57 | | - # Check for suspicious scripts that could be used for attacks |
58 | | - $packageJson = Get-Content package.json | ConvertFrom-Json |
59 | | - $suspiciousScripts = @('preinstall', 'postinstall', 'prepack', 'postpack') |
60 | | - |
61 | | - foreach ($script in $suspiciousScripts) { |
62 | | - if ($packageJson.scripts.$script) { |
63 | | - Write-Warning "⚠️ Found lifecycle script '$script' in package.json" |
64 | | - Write-Warning "Script content: $($packageJson.scripts.$script)" |
65 | | - Write-Warning "Reviewers: Please verify this script is legitimate" |
66 | | - } |
67 | | - } |
68 | | - shell: pwsh |
69 | | - |
70 | | - - name: Installing node_modules with ci (uses lockfile, ignores scripts) |
71 | | - run: npm ci --ignore-scripts |
72 | | - |
73 | | - - name: Audit dependencies for known vulnerabilities |
74 | | - run: npm audit --audit-level=high |
75 | | - continue-on-error: true |
76 | | - |
77 | | - - name: Build GitHub Action |
78 | | - run: npm run build |
79 | | - |
80 | | - - name: Upload build artifact |
81 | | - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 |
| 45 | + - name: Set up SQL Server (Linux) |
| 46 | + if: runner.os == 'Linux' |
| 47 | + uses: ./.github/actions/setup-sql-linux |
82 | 48 | with: |
83 | | - name: action-build-${{ matrix.os }} |
84 | | - path: | |
85 | | - lib/ |
86 | | - node_modules/ |
87 | | - action.yml |
88 | | - package.json |
89 | | - package-lock.json |
90 | | - retention-days: 1 |
91 | | - |
92 | | - # Deploy job that uses the built artifacts and has access to secrets |
93 | | - deploy: |
94 | | - needs: build |
95 | | - environment: Automation test # this environment requires approval before running the action |
96 | | - runs-on: ${{ matrix.os }} |
97 | | - permissions: |
98 | | - checks: write |
99 | | - id-token: write # This is needed for Azure login with OIDC |
100 | | - continue-on-error: true |
101 | | - strategy: |
102 | | - matrix: |
103 | | - os: [windows-latest, ubuntu-latest] |
104 | | - |
105 | | - env: |
106 | | - TEST_DB: 'SqlActionTest-${{ matrix.os }}' |
107 | | - |
108 | | - steps: |
109 | | - - name: Checkout base repository (for test data only) |
110 | | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 |
| 49 | + sa-password: ${{ env.SA_PASSWORD }} |
111 | 50 |
|
112 | | - - name: Download build artifact |
113 | | - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 |
| 51 | + - name: Set up SQL Server (Windows) |
| 52 | + if: runner.os == 'Windows' |
| 53 | + uses: ./.github/actions/setup-sql-windows |
114 | 54 | with: |
115 | | - name: action-build-${{ matrix.os }} |
116 | | - path: . |
| 55 | + sa-password: ${{ env.SA_PASSWORD }} |
| 56 | + |
| 57 | + - name: Build GitHub Action |
| 58 | + run: npm ci --ignore-scripts && npm run build |
117 | 59 |
|
118 | 60 | - name: Setup .NET |
119 | 61 | uses: actions/setup-dotnet@v4 |
120 | 62 | with: |
121 | | - dotnet-version: '8.x' |
122 | | - - name: Install SqlPackage (Linux only) |
123 | | - if: runner.os == 'Linux' |
124 | | - run: dotnet tool install -g microsoft.sqlpackage |
| 63 | + dotnet-version: '10.x' |
125 | 64 |
|
126 | | - - name: Azure Login |
127 | | - uses: azure/login@a65d910e8af852a8061c627c456678983e180302 # v2.2.0 |
128 | | - with: |
129 | | - client-id: ${{ secrets.AZURE_CLIENT_ID }} |
130 | | - tenant-id: ${{ secrets.AZURE_TENANT_ID }} |
131 | | - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} |
| 65 | + - name: Install SqlPackage |
| 66 | + run: dotnet tool install -g microsoft.sqlpackage |
132 | 67 |
|
133 | | - # Deploy a DACPAC with only a table to server |
| 68 | + # Deploy a DACPAC with only a table to server (sqlpackage creates the DB if needed) |
134 | 69 | - name: Test DACPAC Action |
135 | 70 | uses: ./ |
136 | 71 | with: |
137 | | - connection-string: 'Server=${{ secrets.TEST_SERVER }};Initial Catalog=${{ env.TEST_DB }};Authentication=Active Directory Default;' |
| 72 | + connection-string: '${{ env.BASE_CS }}Initial Catalog=${{ env.TEST_DB }};' |
138 | 73 | path: ./__testdata__/sql-action.dacpac |
139 | 74 | action: 'publish' |
| 75 | + skip-firewall-check: true |
140 | 76 |
|
141 | 77 | # Build and publish sqlproj that should create a new view |
142 | 78 | - name: Test Build and Publish |
143 | 79 | uses: ./ |
144 | 80 | with: |
145 | | - connection-string: 'Server=${{ secrets.TEST_SERVER }};Initial Catalog=${{ env.TEST_DB }};Authentication=Active Directory Default;' |
| 81 | + connection-string: '${{ env.BASE_CS }}Initial Catalog=${{ env.TEST_DB }};' |
146 | 82 | path: ./__testdata__/TestProject/sql-action.sqlproj |
147 | 83 | action: 'publish' |
| 84 | + skip-firewall-check: true |
148 | 85 |
|
149 | 86 | # Execute testsql.sql via script action on server |
150 | 87 | - name: Test SQL Action |
151 | 88 | uses: ./ |
152 | 89 | with: |
153 | | - connection-string: 'Server=${{ secrets.TEST_SERVER }};Initial Catalog=${{ env.TEST_DB }};Authentication=Active Directory Default;' |
| 90 | + connection-string: '${{ env.BASE_CS }}Initial Catalog=${{ env.TEST_DB }};' |
154 | 91 | path: ./__testdata__/testsql.sql |
| 92 | + skip-firewall-check: true |
155 | 93 |
|
156 | 94 | - name: Cleanup Test Database |
157 | 95 | if: always() |
158 | 96 | uses: ./ |
159 | | - with: |
160 | | - connection-string: 'Server=${{ secrets.TEST_SERVER }};Initial Catalog=master;Authentication=Active Directory Default;' |
| 97 | + with: |
| 98 | + connection-string: '${{ env.BASE_CS }}Initial Catalog=master;' |
161 | 99 | path: ./__testdata__/cleanup.sql |
162 | 100 | arguments: '-v DbName="${{ env.TEST_DB }}"' |
| 101 | + skip-firewall-check: true |
| 102 | + |
| 103 | + - name: Stop SQL Server container (Linux) |
| 104 | + if: always() && runner.os == 'Linux' |
| 105 | + run: docker rm -f sqlserver || true |
0 commit comments