mirror of
https://github.com/Azure/k8s-deploy.git
synced 2026-06-27 23:39:27 +08:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb38d42b31 | |||
| 0192893acf | |||
| d4ccdf9b20 | |||
| 5d114285d8 | |||
| a873bcb7ac | |||
| cc6628a38e | |||
| 06c6e5b021 | |||
| e8d601f116 | |||
| ef735e2cde | |||
| 6fc8836682 | |||
| c7bccb6124 | |||
| d4bfad4550 | |||
| 4298bf8626 | |||
| f2b0a5041d | |||
| 031b17e505 | |||
| d31be45ea5 | |||
| 9884b47c86 | |||
| 7da36614a9 | |||
| e17e3d873d | |||
| 2208360a18 |
@@ -16,7 +16,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 #v7.0.0
|
||||||
with:
|
with:
|
||||||
# We must fetch at least the immediate parents so that if this is
|
# We must fetch at least the immediate parents so that if this is
|
||||||
# a pull request then we can checkout the head.
|
# a pull request then we can checkout the head.
|
||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 #v3.29.5
|
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e #v3.29.5
|
||||||
# Override language selection by uncommenting this and choosing your languages
|
# Override language selection by uncommenting this and choosing your languages
|
||||||
# with:
|
# with:
|
||||||
# languages: go, javascript, csharp, python, cpp, java
|
# languages: go, javascript, csharp, python, cpp, java
|
||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@c10b8064de6f491fea524254123dbe5e09572f13 #v3.29.5
|
uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e #v3.29.5
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@@ -46,4 +46,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 #v3.29.5
|
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e #v3.29.5
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ jobs:
|
|||||||
|
|
||||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
- uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
|
||||||
name: Setting issue as idle
|
name: Setting issue as idle
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
operations-per-run: 100
|
operations-per-run: 100
|
||||||
exempt-issue-labels: 'backlog'
|
exempt-issue-labels: 'backlog'
|
||||||
|
|
||||||
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
- uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
|
||||||
name: Setting PR as idle
|
name: Setting PR as idle
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: install deps
|
- name: install deps
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
actions: read
|
actions: read
|
||||||
contents: write
|
contents: write
|
||||||
uses: Azure/action-release-workflows/.github/workflows/release_js_project.yaml@v1
|
uses: Azure/action-release-workflows/.github/workflows/release_js_project.yaml@3c677ba5ab58f5c5c1a6f0cfb176b333b1f27405 # v1
|
||||||
with:
|
with:
|
||||||
changelogPath: ./CHANGELOG.md
|
changelogPath: ./CHANGELOG.md
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
KUBECONFIG: /home/runner/.kube/config
|
KUBECONFIG: /home/runner/.kube/config
|
||||||
NAMESPACE: test-${{ github.run_id }}
|
NAMESPACE: test-${{ github.run_id }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- uses: ./.github/actions/minikube-setup
|
- uses: ./.github/actions/minikube-setup
|
||||||
name: Setup Minikube Environment
|
name: Setup Minikube Environment
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
KUBECONFIG: /home/runner/.kube/config
|
KUBECONFIG: /home/runner/.kube/config
|
||||||
NAMESPACE: test-${{ github.run_id }}
|
NAMESPACE: test-${{ github.run_id }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- uses: ./.github/actions/minikube-setup
|
- uses: ./.github/actions/minikube-setup
|
||||||
name: Setup Minikube Environment
|
name: Setup Minikube Environment
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
KUBECONFIG: /home/runner/.kube/config
|
KUBECONFIG: /home/runner/.kube/config
|
||||||
NAMESPACE: test-${{ github.run_id }}
|
NAMESPACE: test-${{ github.run_id }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- uses: ./.github/actions/minikube-setup
|
- uses: ./.github/actions/minikube-setup
|
||||||
name: Setup Minikube Environment
|
name: Setup Minikube Environment
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
KUBECONFIG: /home/runner/.kube/config
|
KUBECONFIG: /home/runner/.kube/config
|
||||||
NAMESPACE: test-${{ github.run_id }}
|
NAMESPACE: test-${{ github.run_id }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- uses: ./.github/actions/minikube-setup
|
- uses: ./.github/actions/minikube-setup
|
||||||
name: Setup Minikube Environment
|
name: Setup Minikube Environment
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
KUBECONFIG: /home/runner/.kube/config
|
KUBECONFIG: /home/runner/.kube/config
|
||||||
NAMESPACE: test-${{ github.run_id }}
|
NAMESPACE: test-${{ github.run_id }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- uses: ./.github/actions/minikube-setup
|
- uses: ./.github/actions/minikube-setup
|
||||||
name: Setup Minikube Environment
|
name: Setup Minikube Environment
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
KUBECONFIG: /home/runner/.kube/config
|
KUBECONFIG: /home/runner/.kube/config
|
||||||
NAMESPACE: test-${{ github.run_id }}
|
NAMESPACE: test-${{ github.run_id }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- uses: ./.github/actions/minikube-setup
|
- uses: ./.github/actions/minikube-setup
|
||||||
name: Setup Minikube Environment
|
name: Setup Minikube Environment
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
NAMESPACE1: integration-test-namespace1-${{ github.run_id }}
|
NAMESPACE1: integration-test-namespace1-${{ github.run_id }}
|
||||||
NAMESPACE2: integration-test-namespace2-${{ github.run_id }}
|
NAMESPACE2: integration-test-namespace2-${{ github.run_id }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- uses: ./.github/actions/minikube-setup
|
- uses: ./.github/actions/minikube-setup
|
||||||
name: Setup Minikube Environment
|
name: Setup Minikube Environment
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||||
|
|
||||||
- uses: Azure/setup-kubectl@15650b3ad78fff148532a140b8a4c821796b2d7b # v5.0.0
|
- uses: Azure/setup-kubectl@829323503d1be3d00ca8346e5391ca0b07a9ab0d # v5.1.0
|
||||||
name: Install Kubectl
|
name: Install Kubectl
|
||||||
|
|
||||||
- name: Create private AKS cluster and set context
|
- name: Create private AKS cluster and set context
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
KUBECONFIG: /home/runner/.kube/config
|
KUBECONFIG: /home/runner/.kube/config
|
||||||
NAMESPACE: test-${{ github.run_id }}
|
NAMESPACE: test-${{ github.run_id }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- uses: ./.github/actions/minikube-setup
|
- uses: ./.github/actions/minikube-setup
|
||||||
name: Setup Minikube Environment
|
name: Setup Minikube Environment
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
name: Run Unit Tests
|
name: Run Unit Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- run: |
|
- run: |
|
||||||
npm install
|
npm install
|
||||||
npm test
|
npm test
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ node_modules
|
|||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.idea
|
.idea
|
||||||
|
lib/
|
||||||
|
|
||||||
coverage/
|
coverage/
|
||||||
-30493
File diff suppressed because one or more lines are too long
Generated
+296
-286
File diff suppressed because it is too large
Load Diff
+5
-5
@@ -14,23 +14,23 @@
|
|||||||
"prepare": "husky"
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^3.0.0",
|
"@actions/core": "^3.0.1",
|
||||||
"@actions/exec": "^3.0.0",
|
"@actions/exec": "^3.0.0",
|
||||||
"@actions/io": "^3.0.2",
|
"@actions/io": "^3.0.2",
|
||||||
"@actions/tool-cache": "4.0.0",
|
"@actions/tool-cache": "4.0.0",
|
||||||
"@octokit/core": "^7.0.6",
|
"@octokit/core": "^7.0.6",
|
||||||
"@octokit/plugin-retry": "^8.1.0",
|
"@octokit/plugin-retry": "^8.1.0",
|
||||||
"js-yaml": "4.1.1",
|
"js-yaml": "4.2.0",
|
||||||
"minimist": "^1.2.8"
|
"minimist": "^1.2.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/minimist": "^1.2.5",
|
"@types/minimist": "^1.2.5",
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.9.3",
|
||||||
"esbuild": "^0.28",
|
"esbuild": "^0.28",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"prettier": "^3.8.1",
|
"prettier": "^3.8.4",
|
||||||
"typescript": "6.0.2",
|
"typescript": "6.0.3",
|
||||||
"vitest": "^4"
|
"vitest": "^4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+364
-14
@@ -3,12 +3,16 @@ import * as fileUtils from './fileUtils.js'
|
|||||||
|
|
||||||
import * as yaml from 'js-yaml'
|
import * as yaml from 'js-yaml'
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
|
import os from 'node:os'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import {K8sObject} from '../types/k8sObject.js'
|
import {K8sObject} from '../types/k8sObject.js'
|
||||||
|
|
||||||
const sampleYamlUrl =
|
const sampleYamlUrl =
|
||||||
'https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/controllers/nginx-deployment.yaml'
|
'https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/controllers/nginx-deployment.yaml'
|
||||||
describe('File utils', () => {
|
describe('File utils', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
process.env.GITHUB_WORKSPACE ??= process.cwd()
|
||||||
|
})
|
||||||
test('correctly parses a yaml file from a URL', async () => {
|
test('correctly parses a yaml file from a URL', async () => {
|
||||||
const tempFile = await fileUtils.writeYamlFromURLToFile(sampleYamlUrl, 0)
|
const tempFile = await fileUtils.writeYamlFromURLToFile(sampleYamlUrl, 0)
|
||||||
const fileContents = fs.readFileSync(tempFile).toString()
|
const fileContents = fs.readFileSync(tempFile).toString()
|
||||||
@@ -53,7 +57,7 @@ describe('File utils', () => {
|
|||||||
expect(testSearch).toHaveLength(10)
|
expect(testSearch).toHaveLength(10)
|
||||||
expectedManifests.forEach((fileName) => {
|
expectedManifests.forEach((fileName) => {
|
||||||
if (fileName.startsWith('test/unit')) {
|
if (fileName.startsWith('test/unit')) {
|
||||||
expect(testSearch).toContain(fileName)
|
expect(testSearch).toContain(path.resolve(fileName))
|
||||||
} else {
|
} else {
|
||||||
expect(fileName.includes(fileUtils.urlFileKind)).toBe(true)
|
expect(fileName.includes(fileUtils.urlFileKind)).toBe(true)
|
||||||
expect(fileName.startsWith(fileUtils.getTempDirectory()))
|
expect(fileName.startsWith(fileUtils.getTempDirectory()))
|
||||||
@@ -105,20 +109,366 @@ describe('File utils', () => {
|
|||||||
fileUtils.writeYamlFromURLToFile(badUrl, 0)
|
fileUtils.writeYamlFromURLToFile(badUrl, 0)
|
||||||
).rejects.toBeTruthy()
|
).rejects.toBeTruthy()
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
describe('moving files to temp', () => {
|
it('rejects manifest inputs that resolve outside the workspace', async () => {
|
||||||
it('correctly moves the contents of a file to the temporary directory', () => {
|
const originalWs = process.env.GITHUB_WORKSPACE
|
||||||
vi.spyOn(fs, 'writeFileSync').mockImplementation(() => {})
|
const ws = fs.mkdtempSync(path.join(os.tmpdir(), 'ws-'))
|
||||||
vi.spyOn(fs, 'readFileSync').mockImplementation((filename) => {
|
const outside = fs.mkdtempSync(path.join(os.tmpdir(), 'outside-'))
|
||||||
return 'test contents'
|
fs.writeFileSync(path.join(outside, 'secrets.yaml'), 'api_key: x')
|
||||||
})
|
process.env.GITHUB_WORKSPACE = ws
|
||||||
const originalFilePath = path.join('path', 'in', 'repo')
|
try {
|
||||||
|
await expect(
|
||||||
|
fileUtils.getFilesFromDirectoriesAndURLs([outside])
|
||||||
|
).rejects.toThrow(/outside the workspace/)
|
||||||
|
await expect(
|
||||||
|
fileUtils.getFilesFromDirectoriesAndURLs([
|
||||||
|
path.join(outside, 'secrets.yaml')
|
||||||
|
])
|
||||||
|
).rejects.toThrow(/outside the workspace/)
|
||||||
|
} finally {
|
||||||
|
if (originalWs === undefined) delete process.env.GITHUB_WORKSPACE
|
||||||
|
else process.env.GITHUB_WORKSPACE = originalWs
|
||||||
|
fs.rmSync(ws, {recursive: true, force: true})
|
||||||
|
fs.rmSync(outside, {recursive: true, force: true})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const output = fileUtils.moveFileToTmpDir(originalFilePath)
|
it('rejects symlinks inside a directory that escape the workspace', async () => {
|
||||||
|
const originalWs = process.env.GITHUB_WORKSPACE
|
||||||
expect(output).toEqual(
|
const ws = fs.mkdtempSync(path.join(os.tmpdir(), 'ws-'))
|
||||||
path.join(fileUtils.getTempDirectory(), '/path/in/repo')
|
const outside = fs.mkdtempSync(path.join(os.tmpdir(), 'outside-'))
|
||||||
)
|
const escapeTarget = path.join(outside, 'passwd.yaml')
|
||||||
|
fs.writeFileSync(escapeTarget, 'root:x:0:0')
|
||||||
|
const dir = path.join(ws, 'manifests')
|
||||||
|
fs.mkdirSync(dir)
|
||||||
|
fs.symlinkSync(escapeTarget, path.join(dir, 'escape.yaml'))
|
||||||
|
process.env.GITHUB_WORKSPACE = ws
|
||||||
|
try {
|
||||||
|
await expect(
|
||||||
|
fileUtils.getFilesFromDirectoriesAndURLs([dir])
|
||||||
|
).rejects.toThrow(/outside the workspace/)
|
||||||
|
} finally {
|
||||||
|
if (originalWs === undefined) delete process.env.GITHUB_WORKSPACE
|
||||||
|
else process.env.GITHUB_WORKSPACE = originalWs
|
||||||
|
fs.rmSync(ws, {recursive: true, force: true})
|
||||||
|
fs.rmSync(outside, {recursive: true, force: true})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('moveFileToTmpDir', () => {
|
||||||
|
let workspace: string
|
||||||
|
let originalWorkspace: string | undefined
|
||||||
|
let originalTemp: string | undefined
|
||||||
|
let originalCwd: string
|
||||||
|
let tmpDir: string
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
originalWorkspace = process.env.GITHUB_WORKSPACE
|
||||||
|
originalTemp = process.env.RUNNER_TEMP
|
||||||
|
originalCwd = process.cwd()
|
||||||
|
workspace = fs.mkdtempSync(path.join(os.tmpdir(), 'ws-'))
|
||||||
|
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rt-'))
|
||||||
|
process.env.GITHUB_WORKSPACE = workspace
|
||||||
|
process.env.RUNNER_TEMP = tmpDir
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
process.chdir(originalCwd)
|
||||||
|
if (originalWorkspace === undefined) delete process.env.GITHUB_WORKSPACE
|
||||||
|
else process.env.GITHUB_WORKSPACE = originalWorkspace
|
||||||
|
if (originalTemp === undefined) delete process.env.RUNNER_TEMP
|
||||||
|
else process.env.RUNNER_TEMP = originalTemp
|
||||||
|
fs.rmSync(workspace, {recursive: true, force: true})
|
||||||
|
fs.rmSync(tmpDir, {recursive: true, force: true})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('copies a workspace file to RUNNER_TEMP using a basename-only destination', () => {
|
||||||
|
const src = path.join(workspace, 'svc.yaml')
|
||||||
|
fs.writeFileSync(src, 'kind: Service')
|
||||||
|
|
||||||
|
const out = fileUtils.moveFileToTmpDir(src)
|
||||||
|
|
||||||
|
expect(fs.realpathSync(path.dirname(out))).toBe(fs.realpathSync(tmpDir))
|
||||||
|
expect(path.basename(out)).toMatch(/^svc_\d+_\d+\.yaml$/)
|
||||||
|
expect(fs.readFileSync(out).toString()).toBe('kind: Service')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rejects relative traversal that escapes the workspace', () => {
|
||||||
|
const outside = fs.mkdtempSync(path.join(os.tmpdir(), 'outside-'))
|
||||||
|
fs.writeFileSync(path.join(outside, 'secrets.yaml'), 'api_key: x')
|
||||||
|
process.chdir(workspace)
|
||||||
|
const rel = path.relative(workspace, path.join(outside, 'secrets.yaml'))
|
||||||
|
expect(() => fileUtils.moveFileToTmpDir(rel)).toThrow(
|
||||||
|
/outside the workspace/
|
||||||
|
)
|
||||||
|
fs.rmSync(outside, {recursive: true, force: true})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not collide when two inputs share a basename', () => {
|
||||||
|
const a = path.join(workspace, 'a')
|
||||||
|
const b = path.join(workspace, 'b')
|
||||||
|
fs.mkdirSync(a)
|
||||||
|
fs.mkdirSync(b)
|
||||||
|
fs.writeFileSync(path.join(a, 'svc.yaml'), 'A')
|
||||||
|
fs.writeFileSync(path.join(b, 'svc.yaml'), 'B')
|
||||||
|
|
||||||
|
const outA = fileUtils.moveFileToTmpDir(path.join(a, 'svc.yaml'))
|
||||||
|
const outB = fileUtils.moveFileToTmpDir(path.join(b, 'svc.yaml'))
|
||||||
|
|
||||||
|
expect(outA).not.toBe(outB)
|
||||||
|
expect(fs.readFileSync(outA).toString()).toBe('A')
|
||||||
|
expect(fs.readFileSync(outB).toString()).toBe('B')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('assertPathWithinWorkspace', () => {
|
||||||
|
let workspace: string
|
||||||
|
let outside: string
|
||||||
|
let originalWorkspace: string | undefined
|
||||||
|
let originalCwd: string
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
originalWorkspace = process.env.GITHUB_WORKSPACE
|
||||||
|
originalCwd = process.cwd()
|
||||||
|
workspace = fs.mkdtempSync(path.join(os.tmpdir(), 'ws-'))
|
||||||
|
outside = fs.mkdtempSync(path.join(os.tmpdir(), 'outside-'))
|
||||||
|
process.env.GITHUB_WORKSPACE = workspace
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
process.chdir(originalCwd)
|
||||||
|
if (originalWorkspace === undefined) {
|
||||||
|
delete process.env.GITHUB_WORKSPACE
|
||||||
|
} else {
|
||||||
|
process.env.GITHUB_WORKSPACE = originalWorkspace
|
||||||
|
}
|
||||||
|
fs.rmSync(workspace, {recursive: true, force: true})
|
||||||
|
fs.rmSync(outside, {recursive: true, force: true})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns the resolved path for files inside the workspace', () => {
|
||||||
|
const inside = path.join(workspace, 'a.yaml')
|
||||||
|
fs.writeFileSync(inside, 'kind: X')
|
||||||
|
const result = fileUtils.assertPathWithinWorkspace(inside)
|
||||||
|
expect(result).toBe(fs.realpathSync(inside))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('accepts workspace files whose basename starts with ..', () => {
|
||||||
|
const inside = path.join(workspace, '..bar.yaml')
|
||||||
|
fs.writeFileSync(inside, 'kind: X')
|
||||||
|
expect(fileUtils.assertPathWithinWorkspace(inside)).toBe(
|
||||||
|
fs.realpathSync(inside)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws for relative traversal paths that escape the workspace', () => {
|
||||||
|
const target = path.join(outside, 'secrets.yaml')
|
||||||
|
fs.writeFileSync(target, 'api_key: secret')
|
||||||
|
const rel = path.relative(workspace, target)
|
||||||
|
process.chdir(workspace)
|
||||||
|
expect(() => fileUtils.assertPathWithinWorkspace(rel)).toThrow(
|
||||||
|
/outside the workspace/
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('resolves relative paths against GITHUB_WORKSPACE even when CWD differs', () => {
|
||||||
|
const inside = path.join(workspace, 'manifest.yaml')
|
||||||
|
fs.writeFileSync(inside, 'kind: X')
|
||||||
|
// Deliberately chdir somewhere unrelated so a process.cwd()-based
|
||||||
|
// resolver would either reject or resolve to the wrong place.
|
||||||
|
process.chdir(os.tmpdir())
|
||||||
|
const result = fileUtils.assertPathWithinWorkspace('manifest.yaml')
|
||||||
|
expect(result).toBe(fs.realpathSync(inside))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws for absolute paths outside the workspace', () => {
|
||||||
|
const target = path.join(outside, 'secrets.yaml')
|
||||||
|
fs.writeFileSync(target, 'api_key: secret')
|
||||||
|
expect(() => fileUtils.assertPathWithinWorkspace(target)).toThrow(
|
||||||
|
/outside the workspace/
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws when a symlink inside the workspace points outside', () => {
|
||||||
|
const target = path.join(outside, 'secrets.yaml')
|
||||||
|
fs.writeFileSync(target, 'api_key: secret')
|
||||||
|
const link = path.join(workspace, 'evil.yaml')
|
||||||
|
fs.symlinkSync(target, link)
|
||||||
|
expect(() => fileUtils.assertPathWithinWorkspace(link)).toThrow(
|
||||||
|
/outside the workspace/
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws a clear error for missing files', () => {
|
||||||
|
const missing = path.join(workspace, 'nope.yaml')
|
||||||
|
expect(() => fileUtils.assertPathWithinWorkspace(missing)).toThrow(
|
||||||
|
/does not exist or is not readable/
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('skips containment when GITHUB_WORKSPACE is unset', () => {
|
||||||
|
delete process.env.GITHUB_WORKSPACE
|
||||||
|
const target = path.join(outside, 'whatever.yaml')
|
||||||
|
fs.writeFileSync(target, 'kind: X')
|
||||||
|
expect(fileUtils.assertPathWithinWorkspace(target)).toBe(target)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
import {EventEmitter} from 'node:events'
|
||||||
|
import {PassThrough} from 'node:stream'
|
||||||
|
import * as https from 'node:https'
|
||||||
|
|
||||||
|
const httpsState = vi.hoisted(() => ({impl: null as any}))
|
||||||
|
|
||||||
|
vi.mock('https', async (importOriginal) => {
|
||||||
|
const actual = await importOriginal<typeof import('https')>()
|
||||||
|
const get = (...args: any[]) =>
|
||||||
|
httpsState.impl ? httpsState.impl(...args) : (actual.get as any)(...args)
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
default: {...actual, get},
|
||||||
|
get
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('writeYamlFromURLToFile error handling', () => {
|
||||||
|
let tempDir: string
|
||||||
|
let originalRunnerTemp: string | undefined
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
originalRunnerTemp = process.env.RUNNER_TEMP
|
||||||
|
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'url-fetch-'))
|
||||||
|
process.env.RUNNER_TEMP = tempDir
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
httpsState.impl = null
|
||||||
|
vi.restoreAllMocks()
|
||||||
|
if (originalRunnerTemp === undefined) {
|
||||||
|
delete process.env.RUNNER_TEMP
|
||||||
|
} else {
|
||||||
|
process.env.RUNNER_TEMP = originalRunnerTemp
|
||||||
|
}
|
||||||
|
fs.rmSync(tempDir, {recursive: true, force: true})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Wait one tick so cleanupAndReject's async fs.rm callback can fire
|
||||||
|
// before the test inspects the temp directory.
|
||||||
|
const waitForCleanup = () =>
|
||||||
|
new Promise<void>((r) => setImmediate(() => setImmediate(r)))
|
||||||
|
|
||||||
|
function mockHttpsGet(
|
||||||
|
makeResponse: () => {
|
||||||
|
response: EventEmitter & {
|
||||||
|
statusCode?: number
|
||||||
|
statusMessage?: string
|
||||||
|
pipe: PassThrough['pipe']
|
||||||
|
resume: () => void
|
||||||
|
}
|
||||||
|
requestEmitter: EventEmitter
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
httpsState.impl = ((url: string, cb?: any) => {
|
||||||
|
const {response, requestEmitter} = makeResponse()
|
||||||
|
if (cb) setImmediate(() => cb(response))
|
||||||
|
return requestEmitter as any
|
||||||
|
}) as any
|
||||||
|
}
|
||||||
|
|
||||||
|
it('rejects on HTTP 500 without writing a file', async () => {
|
||||||
|
const requestEmitter = new EventEmitter()
|
||||||
|
const response = Object.assign(new PassThrough(), {
|
||||||
|
statusCode: 500,
|
||||||
|
statusMessage: 'Server Error',
|
||||||
|
resume() {
|
||||||
|
/* drain */
|
||||||
|
}
|
||||||
|
})
|
||||||
|
mockHttpsGet(() => ({response: response as any, requestEmitter}))
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
fileUtils.writeYamlFromURLToFile('https://example.com/x.yaml', 99)
|
||||||
|
).rejects.toThrow(/Server Error/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rejects when the response stream errors mid-download', async () => {
|
||||||
|
const requestEmitter = new EventEmitter()
|
||||||
|
const response = Object.assign(new PassThrough(), {
|
||||||
|
statusCode: 200,
|
||||||
|
statusMessage: 'OK',
|
||||||
|
resume() {}
|
||||||
|
})
|
||||||
|
mockHttpsGet(() => ({response: response as any, requestEmitter}))
|
||||||
|
|
||||||
|
const p = fileUtils.writeYamlFromURLToFile(
|
||||||
|
'https://example.com/y.yaml',
|
||||||
|
100
|
||||||
|
)
|
||||||
|
setImmediate(() => response.emit('error', new Error('socket reset')))
|
||||||
|
await expect(p).rejects.toThrow(/socket reset/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rejects on request-level errors', async () => {
|
||||||
|
const requestEmitter = new EventEmitter()
|
||||||
|
const response = Object.assign(new PassThrough(), {
|
||||||
|
statusCode: 200,
|
||||||
|
resume() {}
|
||||||
|
})
|
||||||
|
mockHttpsGet(() => ({response: response as any, requestEmitter}))
|
||||||
|
|
||||||
|
const p = fileUtils.writeYamlFromURLToFile(
|
||||||
|
'https://example.com/z.yaml',
|
||||||
|
101
|
||||||
|
)
|
||||||
|
setImmediate(() => requestEmitter.emit('error', new Error('DNS failure')))
|
||||||
|
await expect(p).rejects.toThrow(/DNS failure/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('removes temp file when verification fails', async () => {
|
||||||
|
const requestEmitter = new EventEmitter()
|
||||||
|
const response = Object.assign(new PassThrough(), {
|
||||||
|
statusCode: 200,
|
||||||
|
statusMessage: 'OK'
|
||||||
|
})
|
||||||
|
mockHttpsGet(() => ({response: response as any, requestEmitter}))
|
||||||
|
|
||||||
|
const before = new Set(fs.readdirSync(tempDir))
|
||||||
|
const p = fileUtils.writeYamlFromURLToFile(
|
||||||
|
'https://example.com/bad.yaml',
|
||||||
|
200
|
||||||
|
)
|
||||||
|
// Stream a YAML document missing required k8s fields so verifyYaml fails.
|
||||||
|
setImmediate(() => {
|
||||||
|
response.end('not: a-real-manifest\n')
|
||||||
|
})
|
||||||
|
await expect(p).rejects.toThrow(/missing fields|failed to parse/)
|
||||||
|
await waitForCleanup()
|
||||||
|
const after = fs.readdirSync(tempDir).filter((f) => !before.has(f))
|
||||||
|
expect(after).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('removes temp file on mid-stream response error', async () => {
|
||||||
|
const requestEmitter = new EventEmitter()
|
||||||
|
const response = Object.assign(new PassThrough(), {
|
||||||
|
statusCode: 200,
|
||||||
|
statusMessage: 'OK',
|
||||||
|
resume() {}
|
||||||
|
})
|
||||||
|
mockHttpsGet(() => ({response: response as any, requestEmitter}))
|
||||||
|
|
||||||
|
const before = new Set(fs.readdirSync(tempDir))
|
||||||
|
const p = fileUtils.writeYamlFromURLToFile(
|
||||||
|
'https://example.com/midstream.yaml',
|
||||||
|
201
|
||||||
|
)
|
||||||
|
setImmediate(() => {
|
||||||
|
response.write('kind: Foo\n')
|
||||||
|
response.emit('error', new Error('socket reset'))
|
||||||
|
})
|
||||||
|
await expect(p).rejects.toThrow(/socket reset/)
|
||||||
|
await waitForCleanup()
|
||||||
|
const after = fs.readdirSync(tempDir).filter((f) => !before.has(f))
|
||||||
|
expect(after).toEqual([])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
+87
-28
@@ -11,10 +11,56 @@ import {K8sObject} from '../types/k8sObject.js'
|
|||||||
|
|
||||||
export const urlFileKind = 'urlfile'
|
export const urlFileKind = 'urlfile'
|
||||||
|
|
||||||
|
let moveCounter = 0
|
||||||
|
|
||||||
export function getTempDirectory(): string {
|
export function getTempDirectory(): string {
|
||||||
return process.env['RUNNER_TEMP'] || os.tmpdir()
|
return process.env['RUNNER_TEMP'] || os.tmpdir()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exported for tests. Validates that `inputPath` resolves (after symlink
|
||||||
|
// resolution) to a location inside GITHUB_WORKSPACE. When GITHUB_WORKSPACE
|
||||||
|
// is not set (e.g. local dev / unit tests), the check is skipped — callers
|
||||||
|
// that write to RUNNER_TEMP still get protection from basename-only
|
||||||
|
// destinations.
|
||||||
|
export function assertPathWithinWorkspace(inputPath: string): string {
|
||||||
|
const workspace = process.env.GITHUB_WORKSPACE
|
||||||
|
if (!workspace) {
|
||||||
|
core.warning(
|
||||||
|
'GITHUB_WORKSPACE is not set; skipping manifest path containment check'
|
||||||
|
)
|
||||||
|
return inputPath
|
||||||
|
}
|
||||||
|
const resolvedWorkspace = fs.realpathSync(path.resolve(workspace))
|
||||||
|
// Resolve relative inputs against the workspace (not process.cwd()), so
|
||||||
|
// a relative `manifests:` input is interpreted consistently regardless of
|
||||||
|
// whether a prior step changed the working directory. Absolute paths are
|
||||||
|
// passed through unchanged and still validated below.
|
||||||
|
const absoluteInput = path.isAbsolute(inputPath)
|
||||||
|
? inputPath
|
||||||
|
: path.resolve(resolvedWorkspace, inputPath)
|
||||||
|
let resolvedInput: string
|
||||||
|
try {
|
||||||
|
resolvedInput = fs.realpathSync(absoluteInput)
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(
|
||||||
|
`manifest path ${inputPath} does not exist or is not readable: ${e}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const rel = path.relative(resolvedWorkspace, resolvedInput)
|
||||||
|
if (
|
||||||
|
rel === '' ||
|
||||||
|
(rel !== '..' &&
|
||||||
|
!rel.startsWith('..' + path.sep) &&
|
||||||
|
!path.isAbsolute(rel))
|
||||||
|
) {
|
||||||
|
return resolvedInput
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`manifest path ${inputPath} resolves to ${resolvedInput}, ` +
|
||||||
|
`which is outside the workspace ${resolvedWorkspace}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function writeObjectsToFile(inputObjects: any[]): string[] {
|
export function writeObjectsToFile(inputObjects: any[]): string[] {
|
||||||
const newFilePaths = []
|
const newFilePaths = []
|
||||||
|
|
||||||
@@ -64,22 +110,20 @@ export function writeManifestToFile(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function moveFileToTmpDir(originalFilepath: string) {
|
export function moveFileToTmpDir(originalFilepath: string) {
|
||||||
|
const safeSource = assertPathWithinWorkspace(originalFilepath)
|
||||||
const tempDirectory = getTempDirectory()
|
const tempDirectory = getTempDirectory()
|
||||||
const newPath = path.join(tempDirectory, originalFilepath)
|
const ext = path.extname(safeSource)
|
||||||
|
const base = path.basename(safeSource, ext)
|
||||||
|
const uniqueName = `${base}_${getCurrentTime()}_${moveCounter++}${ext}`
|
||||||
|
const newPath = path.join(tempDirectory, uniqueName)
|
||||||
|
|
||||||
core.debug(`reading original contents from path: ${originalFilepath}`)
|
core.debug(`reading original contents from path: ${originalFilepath}`)
|
||||||
const contents = fs.readFileSync(originalFilepath).toString()
|
const contents = fs.readFileSync(safeSource)
|
||||||
|
|
||||||
const dirName = path.dirname(newPath)
|
|
||||||
if (!fs.existsSync(dirName)) {
|
|
||||||
core.debug(`path ${dirName} doesn't exist yet, making new dir...`)
|
|
||||||
fs.mkdirSync(dirName, {recursive: true})
|
|
||||||
}
|
|
||||||
core.debug(`writing contents to new path ${newPath}`)
|
core.debug(`writing contents to new path ${newPath}`)
|
||||||
fs.writeFileSync(path.join(newPath), contents)
|
fs.writeFileSync(newPath, contents)
|
||||||
|
|
||||||
core.debug(`moved contents from ${originalFilepath} to ${newPath}`)
|
core.debug(`moved contents from ${originalFilepath} to ${newPath}`)
|
||||||
|
|
||||||
return newPath
|
return newPath
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,15 +153,20 @@ export async function getFilesFromDirectoriesAndURLs(
|
|||||||
`encountered error trying to pull YAML from URL ${fileName}: ${e}`
|
`encountered error trying to pull YAML from URL ${fileName}: ${e}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (fs.lstatSync(fileName).isDirectory()) {
|
continue
|
||||||
recurisveManifestGetter(fileName).forEach((file) => {
|
}
|
||||||
|
|
||||||
|
const safePath = assertPathWithinWorkspace(fileName)
|
||||||
|
|
||||||
|
if (fs.lstatSync(safePath).isDirectory()) {
|
||||||
|
recurisveManifestGetter(safePath).forEach((file) => {
|
||||||
fullPathSet.add(file)
|
fullPathSet.add(file)
|
||||||
})
|
})
|
||||||
} else if (
|
} else if (
|
||||||
getFileExtension(fileName) === 'yml' ||
|
getFileExtension(safePath) === 'yml' ||
|
||||||
getFileExtension(fileName) === 'yaml'
|
getFileExtension(safePath) === 'yaml'
|
||||||
) {
|
) {
|
||||||
fullPathSet.add(fileName)
|
fullPathSet.add(safePath)
|
||||||
} else {
|
} else {
|
||||||
core.debug(
|
core.debug(
|
||||||
`Detected non-manifest file, ${fileName}, continuing... `
|
`Detected non-manifest file, ${fileName}, continuing... `
|
||||||
@@ -140,24 +189,33 @@ export async function writeYamlFromURLToFile(
|
|||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
https
|
https
|
||||||
.get(url, async (response) => {
|
.get(url, (response) => {
|
||||||
const code = response.statusCode ?? 0
|
const code = response.statusCode ?? 0
|
||||||
if (code >= 400) {
|
if (code >= 400) {
|
||||||
|
response.resume()
|
||||||
reject(
|
reject(
|
||||||
Error(
|
new Error(
|
||||||
`received response status ${response.statusMessage} from url ${url}`
|
`received response status ${response.statusMessage} from url ${url}`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetPath = getNewTempManifestFileName(
|
const targetPath = getNewTempManifestFileName(
|
||||||
urlFileKind,
|
urlFileKind,
|
||||||
fileNumber.toString()
|
fileNumber.toString()
|
||||||
)
|
)
|
||||||
// save the file to disk
|
// Once the write stream is created the file exists on disk;
|
||||||
const fileWriter = fs
|
// route all post-stream rejections through this helper so we
|
||||||
.createWriteStream(targetPath)
|
// don't leave truncated YAML in RUNNER_TEMP for later tooling
|
||||||
.on('finish', () => {
|
// to pick up. Do NOT unlink on the success path.
|
||||||
|
const cleanupAndReject = (err: unknown) => {
|
||||||
|
fs.rm(targetPath, {force: true}, () => reject(err))
|
||||||
|
}
|
||||||
|
const fileWriter = fs.createWriteStream(targetPath)
|
||||||
|
fileWriter.on('error', cleanupAndReject)
|
||||||
|
fileWriter.on('finish', () => {
|
||||||
|
try {
|
||||||
const verification = verifyYaml(targetPath, url)
|
const verification = verifyYaml(targetPath, url)
|
||||||
if (succeeded(verification)) {
|
if (succeeded(verification)) {
|
||||||
core.debug(
|
core.debug(
|
||||||
@@ -167,15 +225,16 @@ export async function writeYamlFromURLToFile(
|
|||||||
)
|
)
|
||||||
resolve(targetPath)
|
resolve(targetPath)
|
||||||
} else {
|
} else {
|
||||||
reject(verification.error)
|
cleanupAndReject(new Error(verification.error))
|
||||||
}
|
}
|
||||||
})
|
} catch (e) {
|
||||||
|
cleanupAndReject(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
response.on('error', cleanupAndReject)
|
||||||
response.pipe(fileWriter)
|
response.pipe(fileWriter)
|
||||||
})
|
})
|
||||||
.on('error', (error) => {
|
.on('error', reject)
|
||||||
reject(error)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +258,7 @@ function verifyYaml(filepath: string, url: string): Errorable<K8sObject[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const obj of inputObjects) {
|
for (const obj of inputObjects) {
|
||||||
if (!obj.kind || !obj.apiVersion || !obj.metadata) {
|
if (obj == null || !obj.kind || !obj.apiVersion || !obj.metadata) {
|
||||||
return {
|
return {
|
||||||
succeeded: false,
|
succeeded: false,
|
||||||
error: `failed to parse manifest from ${url}: missing fields`
|
error: `failed to parse manifest from ${url}: missing fields`
|
||||||
@@ -221,7 +280,7 @@ function recurisveManifestGetter(dirName: string): string[] {
|
|||||||
getFileExtension(fileName) === 'yml' ||
|
getFileExtension(fileName) === 'yml' ||
|
||||||
getFileExtension(fileName) === 'yaml'
|
getFileExtension(fileName) === 'yaml'
|
||||||
) {
|
) {
|
||||||
toRet.push(path.join(dirName, fileName))
|
toRet.push(assertPathWithinWorkspace(fnwd))
|
||||||
} else {
|
} else {
|
||||||
core.debug(`Detected non-manifest file, ${fileName}, continuing... `)
|
core.debug(`Detected non-manifest file, ${fileName}, continuing... `)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user