mirror of
https://github.com/Azure/k8s-deploy.git
synced 2026-06-21 18:59:27 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 17f5181337 | |||
| e3c97bfc20 |
@@ -21,11 +21,15 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Building latest changes
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm -rf node_modules/
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
- name: Install ncc
|
||||
run: npm i -g @vercel/ncc
|
||||
- name: Build
|
||||
run: ncc build src/run.ts -o lib
|
||||
|
||||
- name: Set name of ns
|
||||
run: echo "::set-output name=name::$(echo `date +%Y%m%d%H%M%S`)"
|
||||
|
||||
@@ -42,7 +42,7 @@ Following are the key capabilities of this action:
|
||||
</tr>
|
||||
<tr>
|
||||
<td>manifests </br></br>(Required)</td>
|
||||
<td>Path to the manifest files to be used for deployment</td>
|
||||
<td>Path to the manifest files to be used for deployment. These can also be directories containing manifest files, in which case, all manifest files in the referenced directory at every depth will be deployed. Files not ending in .yml or .yaml will be ignored.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>namespace </br></br>(Optional)
|
||||
@@ -57,6 +57,10 @@ Following are the key capabilities of this action:
|
||||
<tr>
|
||||
<td>imagepullsecrets </br></br>(Optional)</td>
|
||||
<td>Multiline input where each line contains the name of a docker-registry secret that has already been setup within the cluster. Each of these secret names are added under imagePullSecrets field for the workloads found in the input manifest files</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>pull-images</br></br>(Optional)</td>
|
||||
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to pull the images from the registry before deployment to find out Dockerfile's path in order to add it to the annotations</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>strategy </br></br>(Optional)</td>
|
||||
@@ -90,6 +94,10 @@ Following are the key capabilities of this action:
|
||||
<td>force </br></br>(Optional)</td>
|
||||
<td>Deploy when a previous deployment already exists. If true then '--force' argument is added to the apply command. Using '--force' argument is not recommended in production.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>annotate-namespace</br></br>(Optional)</td>
|
||||
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to annotate the namespace resources object or not</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Usage Examples
|
||||
@@ -97,23 +105,21 @@ Following are the key capabilities of this action:
|
||||
### Basic deployment (without any deployment strategy)
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v1.4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: "myapp"
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
dir/manifestsDirectory
|
||||
images: "contoso.azurecr.io/myapp:${{ event.run_id }}"
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
kubectl-version: "latest"
|
||||
```
|
||||
|
||||
### Canary deployment without service mesh
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v1.4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: "myapp"
|
||||
images: "contoso.azurecr.io/myapp:${{ event.run_id }}"
|
||||
@@ -123,6 +129,7 @@ Following are the key capabilities of this action:
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
dir/manifestsDirectory
|
||||
strategy: canary
|
||||
action: deploy
|
||||
percentage: 20
|
||||
@@ -131,7 +138,7 @@ Following are the key capabilities of this action:
|
||||
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v1.4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: "myapp"
|
||||
images: "contoso.azurecr.io/myapp:${{ event.run_id }}"
|
||||
@@ -141,6 +148,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
dir/manifestsDirectory
|
||||
strategy: canary
|
||||
action: promote # substitute reject if you want to reject
|
||||
```
|
||||
@@ -148,7 +156,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
||||
### Canary deployment based on Service Mesh Interface
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v1.4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: "myapp"
|
||||
images: "contoso.azurecr.io/myapp:${{ event.run_id }}"
|
||||
@@ -158,6 +166,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
dir/manifestsDirectory
|
||||
strategy: canary
|
||||
action: deploy
|
||||
traffic-split-method: smi
|
||||
@@ -168,7 +177,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
||||
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v1.4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: "myapp"
|
||||
images: "contoso.azurecr.io/myapp:${{ event.run_id }} "
|
||||
@@ -178,6 +187,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
dir/manifestsDirectory
|
||||
strategy: canary
|
||||
traffic-split-method: smi
|
||||
action: reject # substitute reject if you want to reject
|
||||
@@ -186,7 +196,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
||||
### Blue-Green deployment with different route methods
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v1.4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: "myapp"
|
||||
images: "contoso.azurecr.io/myapp:${{ event.run_id }}"
|
||||
@@ -206,7 +216,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
||||
To promote/reject the green workload created by the above snippet, the following YAML snippet could be used:
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v1.4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: "myapp"
|
||||
images: "contoso.azurecr.io/myapp:${{ event.run_id }}"
|
||||
@@ -216,7 +226,7 @@ To promote/reject the green workload created by the above snippet, the following
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
ingress-yml
|
||||
ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress # should be the same as the value when action was deploy
|
||||
action: promote # substitute reject if you want to reject
|
||||
@@ -263,7 +273,7 @@ jobs:
|
||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
secret-name: demo-k8s-secret
|
||||
|
||||
- uses: Azure/k8s-deploy@v1.4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
action: deploy
|
||||
manifests: |
|
||||
@@ -309,7 +319,7 @@ jobs:
|
||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
secret-name: demo-k8s-secret
|
||||
|
||||
- uses: Azure/k8s-deploy@v1.4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
action: deploy
|
||||
manifests: |
|
||||
|
||||
@@ -15,6 +15,10 @@ inputs:
|
||||
imagepullsecrets:
|
||||
description: "Name of a docker-registry secret that has already been set up within the cluster. Each of these secret names are added under imagePullSecrets field for the workloads found in the input manifest files"
|
||||
required: false
|
||||
pull-images:
|
||||
description: "Switch whether to pull the images from the registry before deployment to find out Dockerfile's path in order to add it to the annotations"
|
||||
required: false
|
||||
default: true
|
||||
strategy:
|
||||
description: "Deployment strategy to be used. Allowed values are none, canary and blue-green"
|
||||
required: false
|
||||
@@ -51,6 +55,10 @@ inputs:
|
||||
description: "Github token"
|
||||
default: ${{ github.token }}
|
||||
required: true
|
||||
annotate-namespace:
|
||||
description: "Annotate the target namespace"
|
||||
required: false
|
||||
default: true
|
||||
|
||||
branding:
|
||||
color: "green"
|
||||
|
||||
+71
-14
@@ -18192,6 +18192,7 @@ const promote_1 = __nccwpck_require__(3604);
|
||||
const reject_1 = __nccwpck_require__(7530);
|
||||
const action_1 = __nccwpck_require__(2868);
|
||||
const deploymentStrategy_1 = __nccwpck_require__(7023);
|
||||
const fileUtils_1 = __nccwpck_require__(7446);
|
||||
function run() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
// verify kubeconfig is set
|
||||
@@ -18205,6 +18206,7 @@ function run() {
|
||||
.split(/[\n,;]+/) // split into each individual manifest
|
||||
.map((manifest) => manifest.trim()) // remove surrounding whitespace
|
||||
.filter((manifest) => manifest.length > 0); // remove any blanks
|
||||
const fullManifestFilePaths = fileUtils_1.getFilesFromDirectories(manifestFilePaths);
|
||||
// create kubectl
|
||||
const kubectlPath = yield kubectl_1.getKubectlPath();
|
||||
const namespace = core.getInput("namespace") || "default";
|
||||
@@ -18212,15 +18214,15 @@ function run() {
|
||||
// run action
|
||||
switch (action) {
|
||||
case action_1.Action.DEPLOY: {
|
||||
yield deploy_1.deploy(kubectl, manifestFilePaths, strategy);
|
||||
yield deploy_1.deploy(kubectl, fullManifestFilePaths, strategy);
|
||||
break;
|
||||
}
|
||||
case action_1.Action.PROMOTE: {
|
||||
yield promote_1.promote(kubectl, manifestFilePaths, strategy);
|
||||
yield promote_1.promote(kubectl, fullManifestFilePaths, strategy);
|
||||
break;
|
||||
}
|
||||
case action_1.Action.REJECT: {
|
||||
yield reject_1.reject(kubectl, manifestFilePaths, strategy);
|
||||
yield reject_1.reject(kubectl, fullManifestFilePaths, strategy);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -19583,7 +19585,10 @@ function annotateResources(files, kubectl, resourceTypes, allPods, annotationKey
|
||||
const namespace = core.getInput("namespace") || "default";
|
||||
const lastSuccessSha = yield kubectlUtils_1.getLastSuccessfulRunSha(kubectl, namespace, annotationKey);
|
||||
const annotationKeyValStr = `${annotationKey}=${workflowAnnotationUtils_1.getWorkflowAnnotations(lastSuccessSha, workflowFilePath, deploymentConfig)}`;
|
||||
annotateResults.push(yield kubectl.annotate("namespace", namespace, annotationKeyValStr));
|
||||
const annotateNamespace = !(core.getInput("annotate-namespace").toLowerCase() === "false");
|
||||
if (annotateNamespace) {
|
||||
annotateResults.push(yield kubectl.annotate("namespace", namespace, annotationKeyValStr));
|
||||
}
|
||||
annotateResults.push(yield kubectl.annotateFiles(files, annotationKeyValStr));
|
||||
for (const resource of resourceTypes) {
|
||||
if (resource.type.toLowerCase() !==
|
||||
@@ -20082,13 +20087,16 @@ function getDeploymentConfig() {
|
||||
}
|
||||
const imageNames = core.getInput("images").split("\n") || [];
|
||||
const imageDockerfilePathMap = {};
|
||||
//Fetching from image label if available
|
||||
for (const image of imageNames) {
|
||||
try {
|
||||
imageDockerfilePathMap[image] = yield getDockerfilePath(image);
|
||||
}
|
||||
catch (ex) {
|
||||
core.warning(`Failed to get dockerfile path for image ${image.toString()}: ${ex} `);
|
||||
const pullImages = !(core.getInput("pull-images").toLowerCase() === "false");
|
||||
if (pullImages) {
|
||||
//Fetching from image label if available
|
||||
for (const image of imageNames) {
|
||||
try {
|
||||
imageDockerfilePathMap[image] = yield getDockerfilePath(image);
|
||||
}
|
||||
catch (ex) {
|
||||
core.warning(`Failed to get dockerfile path for image ${image.toString()}: ${ex} `);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve({
|
||||
@@ -20135,7 +20143,7 @@ exports.checkDockerPath = checkDockerPath;
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.writeManifestToFile = exports.writeObjectsToFile = exports.getTempDirectory = void 0;
|
||||
exports.getFilesFromDirectories = exports.writeManifestToFile = exports.writeObjectsToFile = exports.getTempDirectory = void 0;
|
||||
const fs = __nccwpck_require__(7147);
|
||||
const path = __nccwpck_require__(1017);
|
||||
const core = __nccwpck_require__(6024);
|
||||
@@ -20186,6 +20194,46 @@ function getManifestFileName(kind, name) {
|
||||
const tempDirectory = getTempDirectory();
|
||||
return path.join(tempDirectory, path.basename(filePath));
|
||||
}
|
||||
function getFilesFromDirectories(filePaths) {
|
||||
const fullPathSet = new Set();
|
||||
filePaths.forEach((fileName => {
|
||||
try {
|
||||
if (fs.lstatSync(fileName).isDirectory()) {
|
||||
recurisveManifestGetter(fileName).forEach((file) => { fullPathSet.add(file); });
|
||||
}
|
||||
else if (getFileExtension(fileName) === "yml" || getFileExtension(fileName) === "yaml") {
|
||||
fullPathSet.add(fileName);
|
||||
}
|
||||
else {
|
||||
core.debug(`Detected non-manifest file, ${fileName}, continuing... `);
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
throw Error(`Exception occurred while reading the file ${fileName}: ${ex}`);
|
||||
}
|
||||
}));
|
||||
return Array.from(fullPathSet);
|
||||
}
|
||||
exports.getFilesFromDirectories = getFilesFromDirectories;
|
||||
function recurisveManifestGetter(dirName) {
|
||||
const toRet = [];
|
||||
fs.readdirSync(dirName).forEach((fileName) => {
|
||||
const fnwd = path.join(dirName, fileName);
|
||||
if (fs.lstatSync(fnwd).isDirectory()) {
|
||||
toRet.push(...recurisveManifestGetter(fnwd));
|
||||
}
|
||||
else if (getFileExtension(fileName) === "yml" || getFileExtension(fileName) === "yaml") {
|
||||
toRet.push(path.join(dirName, fileName));
|
||||
}
|
||||
else {
|
||||
core.debug(`Detected non-manifest file, ${fileName}, continuing... `);
|
||||
}
|
||||
});
|
||||
return toRet;
|
||||
}
|
||||
function getFileExtension(fileName) {
|
||||
return fileName.slice((fileName.lastIndexOf(".") - 1 >>> 0) + 2);
|
||||
}
|
||||
|
||||
|
||||
/***/ }),
|
||||
@@ -20931,7 +20979,15 @@ exports.getTrafficSplitAPIVersion = getTrafficSplitAPIVersion;
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.getWorkflowAnnotationKeyLabel = exports.getWorkflowAnnotations = void 0;
|
||||
exports.getWorkflowAnnotationKeyLabel = exports.getWorkflowAnnotations = exports.prefixObjectKeys = void 0;
|
||||
const ANNOTATION_PREFIX = "actions.github.com/";
|
||||
function prefixObjectKeys(obj, prefix) {
|
||||
return Object.keys(obj).reduce((newObj, key) => {
|
||||
newObj[prefix + key] = obj[key];
|
||||
return newObj;
|
||||
}, {});
|
||||
}
|
||||
exports.prefixObjectKeys = prefixObjectKeys;
|
||||
function getWorkflowAnnotations(lastSuccessRunSha, workflowFilePath, deploymentConfig) {
|
||||
const annotationObject = {
|
||||
run: process.env.GITHUB_RUN_ID,
|
||||
@@ -20950,7 +21006,8 @@ function getWorkflowAnnotations(lastSuccessRunSha, workflowFilePath, deploymentC
|
||||
helmChartPaths: deploymentConfig.helmChartFilePaths,
|
||||
provider: "GitHub",
|
||||
};
|
||||
return JSON.stringify(annotationObject);
|
||||
const prefixedAnnotationObject = prefixObjectKeys(annotationObject, ANNOTATION_PREFIX);
|
||||
return JSON.stringify(prefixedAnnotationObject);
|
||||
}
|
||||
exports.getWorkflowAnnotations = getWorkflowAnnotations;
|
||||
function getWorkflowAnnotationKeyLabel(workflowFilePath) {
|
||||
|
||||
+6075
-6075
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
module.exports = ({onlyFirst = false} = {}) => {
|
||||
const pattern = [
|
||||
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
||||
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
||||
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))'
|
||||
].join('|');
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ansi-regex",
|
||||
"version": "5.0.0",
|
||||
"version": "5.0.1",
|
||||
"description": "Regular expression for matching ANSI escape codes",
|
||||
"license": "MIT",
|
||||
"repository": "chalk/ansi-regex",
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# ansi-regex [](https://travis-ci.org/chalk/ansi-regex)
|
||||
# ansi-regex
|
||||
|
||||
> Regular expression for matching [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code)
|
||||
|
||||
|
||||
+6
-2
@@ -70,7 +70,7 @@ module.exports = function (args, opts) {
|
||||
var o = obj;
|
||||
for (var i = 0; i < keys.length-1; i++) {
|
||||
var key = keys[i];
|
||||
if (key === '__proto__') return;
|
||||
if (isConstructorOrProto(o, key)) return;
|
||||
if (o[key] === undefined) o[key] = {};
|
||||
if (o[key] === Object.prototype || o[key] === Number.prototype
|
||||
|| o[key] === String.prototype) o[key] = {};
|
||||
@@ -79,7 +79,7 @@ module.exports = function (args, opts) {
|
||||
}
|
||||
|
||||
var key = keys[keys.length - 1];
|
||||
if (key === '__proto__') return;
|
||||
if (isConstructorOrProto(o, key)) return;
|
||||
if (o === Object.prototype || o === Number.prototype
|
||||
|| o === String.prototype) o = {};
|
||||
if (o === Array.prototype) o = [];
|
||||
@@ -243,3 +243,7 @@ function isNumber (x) {
|
||||
return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
|
||||
}
|
||||
|
||||
|
||||
function isConstructorOrProto (obj, key) {
|
||||
return key === 'constructor' && typeof obj[key] === 'function' || key === '__proto__';
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "minimist",
|
||||
"version": "1.2.5",
|
||||
"version": "1.2.6",
|
||||
"description": "parse argument options",
|
||||
"main": "index.js",
|
||||
"devDependencies": {
|
||||
|
||||
+4
-1
@@ -34,7 +34,10 @@ $ node example/parse.js -x 3 -y 4 -n5 -abc --beep=boop foo bar baz
|
||||
Previous versions had a prototype pollution bug that could cause privilege
|
||||
escalation in some circumstances when handling untrusted user input.
|
||||
|
||||
Please use version 1.2.3 or later: https://snyk.io/vuln/SNYK-JS-MINIMIST-559764
|
||||
Please use version 1.2.6 or later:
|
||||
|
||||
* https://security.snyk.io/vuln/SNYK-JS-MINIMIST-2429795 (version <=1.2.5)
|
||||
* https://snyk.io/vuln/SNYK-JS-MINIMIST-559764 (version <=1.2.3)
|
||||
|
||||
# methods
|
||||
|
||||
|
||||
+16
@@ -42,3 +42,19 @@ test('proto pollution (constructor)', function (t) {
|
||||
t.equal(argv.y, undefined);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('proto pollution (constructor function)', function (t) {
|
||||
var argv = parse(['--_.concat.constructor.prototype.y', '123']);
|
||||
function fnToBeTested() {}
|
||||
t.equal(fnToBeTested.y, undefined);
|
||||
t.equal(argv.y, undefined);
|
||||
t.end();
|
||||
});
|
||||
|
||||
// powered by snyk - https://github.com/backstage/backstage/issues/10343
|
||||
test('proto pollution (constructor function) snyk', function (t) {
|
||||
var argv = parse('--_.constructor.constructor.prototype.foo bar'.split(' '));
|
||||
t.equal((function(){}).foo, undefined);
|
||||
t.equal(argv.y, undefined);
|
||||
t.end();
|
||||
})
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.6
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
var INTERPOLATE = /{([\s\S]+?)}/g
|
||||
var INTERPOLATE = /{([^{]+?)}/g
|
||||
|
||||
module.exports = function(str, data) {
|
||||
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
|
||||
|
||||
+6
-4
@@ -1,17 +1,19 @@
|
||||
{
|
||||
"name": "tmpl",
|
||||
"description": "JavaScript micro templates.",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.5",
|
||||
"license": "BSD-3-Clause",
|
||||
"homepage": "https://github.com/nshah/nodejs-tmpl",
|
||||
"homepage": "https://github.com/daaku/nodejs-tmpl",
|
||||
"author": "Naitik Shah <n@daaku.org>",
|
||||
"main": "lib/tmpl",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/daaku/nodejs-tmpl"
|
||||
},
|
||||
"scripts": { "test": "NODE_PATH=./lib mocha --ui exports" },
|
||||
"scripts": {
|
||||
"test": "NODE_PATH=./lib mocha --ui exports"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "0.12.x"
|
||||
"mocha": "^9.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+10940
-10940
File diff suppressed because it is too large
Load Diff
+6
-4
@@ -5,6 +5,7 @@ import { promote } from "./actions/promote";
|
||||
import { reject } from "./actions/reject";
|
||||
import { Action, parseAction } from "./types/action";
|
||||
import { parseDeploymentStrategy } from "./types/deploymentStrategy";
|
||||
import { getFilesFromDirectories } from "./utilities/fileUtils";
|
||||
|
||||
export async function run() {
|
||||
// verify kubeconfig is set
|
||||
@@ -23,7 +24,8 @@ export async function run() {
|
||||
.split(/[\n,;]+/) // split into each individual manifest
|
||||
.map((manifest) => manifest.trim()) // remove surrounding whitespace
|
||||
.filter((manifest) => manifest.length > 0); // remove any blanks
|
||||
|
||||
|
||||
const fullManifestFilePaths = getFilesFromDirectories(manifestFilePaths)
|
||||
// create kubectl
|
||||
const kubectlPath = await getKubectlPath();
|
||||
const namespace = core.getInput("namespace") || "default";
|
||||
@@ -32,15 +34,15 @@ export async function run() {
|
||||
// run action
|
||||
switch (action) {
|
||||
case Action.DEPLOY: {
|
||||
await deploy(kubectl, manifestFilePaths, strategy);
|
||||
await deploy(kubectl, fullManifestFilePaths, strategy);
|
||||
break;
|
||||
}
|
||||
case Action.PROMOTE: {
|
||||
await promote(kubectl, manifestFilePaths, strategy);
|
||||
await promote(kubectl, fullManifestFilePaths, strategy);
|
||||
break;
|
||||
}
|
||||
case Action.REJECT: {
|
||||
await reject(kubectl, manifestFilePaths, strategy);
|
||||
await reject(kubectl, fullManifestFilePaths, strategy);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
||||
@@ -173,9 +173,13 @@ async function annotateResources(
|
||||
workflowFilePath,
|
||||
deploymentConfig
|
||||
)}`;
|
||||
annotateResults.push(
|
||||
await kubectl.annotate("namespace", namespace, annotationKeyValStr)
|
||||
);
|
||||
|
||||
const annotateNamespace = !(core.getInput("annotate-namespace").toLowerCase() === "false");
|
||||
if (annotateNamespace) {
|
||||
annotateResults.push(
|
||||
await kubectl.annotate("namespace", namespace, annotationKeyValStr)
|
||||
);
|
||||
}
|
||||
annotateResults.push(await kubectl.annotateFiles(files, annotationKeyValStr));
|
||||
|
||||
for (const resource of resourceTypes) {
|
||||
|
||||
@@ -26,14 +26,17 @@ export async function getDeploymentConfig(): Promise<DeploymentConfig> {
|
||||
const imageNames = core.getInput("images").split("\n") || [];
|
||||
const imageDockerfilePathMap: { [id: string]: string } = {};
|
||||
|
||||
//Fetching from image label if available
|
||||
for (const image of imageNames) {
|
||||
try {
|
||||
imageDockerfilePathMap[image] = await getDockerfilePath(image);
|
||||
} catch (ex) {
|
||||
core.warning(
|
||||
`Failed to get dockerfile path for image ${image.toString()}: ${ex} `
|
||||
);
|
||||
const pullImages = !(core.getInput("pull-images").toLowerCase() === "false");
|
||||
if (pullImages) {
|
||||
//Fetching from image label if available
|
||||
for (const image of imageNames) {
|
||||
try {
|
||||
imageDockerfilePathMap[image] = await getDockerfilePath(image);
|
||||
} catch (ex) {
|
||||
core.warning(
|
||||
`Failed to get dockerfile path for image ${image.toString()}: ${ex} `
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import {
|
||||
getFilesFromDirectories
|
||||
} from "./fileUtils";
|
||||
|
||||
import * as path from "path";
|
||||
|
||||
describe("File utils", () => {
|
||||
it("detects files in nested directories and ignores non-manifest files and empty dirs", () => {
|
||||
const testPath = path.join("test", "unit", "manifests")
|
||||
const testSearch: string[] = getFilesFromDirectories([testPath])
|
||||
|
||||
const expectedManifests =
|
||||
[
|
||||
"test/unit/manifests/manifest_test_dir/another_layer/deep-ingress.yaml",
|
||||
"test/unit/manifests/manifest_test_dir/another_layer/deep-service.yaml",
|
||||
"test/unit/manifests/manifest_test_dir/nested-test-service.yaml",
|
||||
"test/unit/manifests/test-ingress.yml",
|
||||
"test/unit/manifests/test-service.yml"
|
||||
]
|
||||
|
||||
|
||||
// is there a more efficient way to test equality w random order?
|
||||
expect(testSearch).toHaveLength(5);
|
||||
expectedManifests.forEach((fileName) => {
|
||||
expect(testSearch).toContain(fileName)
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
it("crashes when an invalid file is provided", () => {
|
||||
const badPath = path.join("test", "unit", "manifests", "nonexistent.yaml")
|
||||
const goodPath = path.join("test", "unit", "manifests", "manifest_test_dir")
|
||||
|
||||
expect(() => {getFilesFromDirectories([badPath, goodPath])}).toThrowError()
|
||||
});
|
||||
|
||||
it("doesn't duplicate files when nested dir included", () => {
|
||||
const outerPath = path.join("test", "unit", "manifests")
|
||||
const fileAtOuter = path.join("test", "unit", "manifests", "test-service.yml")
|
||||
const innerPath = path.join("test", "unit", "manifests", "manifest_test_dir")
|
||||
|
||||
expect(getFilesFromDirectories([outerPath, fileAtOuter, innerPath])).toHaveLength(5)
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// files that don't exist / nested files that don't exist / something else with non-manifest
|
||||
// lots of combinations of pointing to a directory and non yaml/yaml file
|
||||
// similarly named files in different folders
|
||||
@@ -61,3 +61,49 @@ function getManifestFileName(kind: string, name: string) {
|
||||
const tempDirectory = getTempDirectory();
|
||||
return path.join(tempDirectory, path.basename(filePath));
|
||||
}
|
||||
|
||||
export function getFilesFromDirectories(
|
||||
filePaths: string[]
|
||||
): string[]{
|
||||
|
||||
const fullPathSet: Set<string> = new Set<string>()
|
||||
|
||||
filePaths.forEach((fileName => {
|
||||
try {
|
||||
if(fs.lstatSync(fileName).isDirectory()){
|
||||
recurisveManifestGetter(fileName).forEach((file) => {fullPathSet.add(file)})
|
||||
} else if(getFileExtension(fileName) === "yml" || getFileExtension(fileName) === "yaml"){
|
||||
fullPathSet.add(fileName)
|
||||
} else{
|
||||
core.debug(`Detected non-manifest file, ${fileName}, continuing... ` )
|
||||
}
|
||||
} catch (ex) {
|
||||
throw Error(
|
||||
`Exception occurred while reading the file ${fileName}: ${ex}`
|
||||
);
|
||||
}
|
||||
}))
|
||||
|
||||
return Array.from(fullPathSet)
|
||||
}
|
||||
|
||||
function recurisveManifestGetter(dirName: string): string[]{
|
||||
const toRet: string[] = []
|
||||
|
||||
fs.readdirSync(dirName).forEach((fileName) => {
|
||||
const fnwd: string = path.join(dirName, fileName)
|
||||
if(fs.lstatSync(fnwd).isDirectory()){
|
||||
toRet.push(...recurisveManifestGetter(fnwd))
|
||||
} else if(getFileExtension(fileName) === "yml" || getFileExtension(fileName) === "yaml"){
|
||||
toRet.push(path.join(dirName, fileName))
|
||||
} else{
|
||||
core.debug(`Detected non-manifest file, ${fileName}, continuing... ` )
|
||||
}
|
||||
})
|
||||
|
||||
return toRet
|
||||
}
|
||||
|
||||
function getFileExtension(fileName: string){
|
||||
return fileName.slice((fileName.lastIndexOf(".") - 1 >>> 0) + 2)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { prefixObjectKeys } from "../utilities/workflowAnnotationUtils";
|
||||
|
||||
describe("WorkflowAnnotationUtils", () => {
|
||||
describe("prefixObjectKeys", () => {
|
||||
it("should prefix an object with a given prefix", () => {
|
||||
const obj = {
|
||||
foo: "bar",
|
||||
baz: "qux",
|
||||
};
|
||||
const prefix = "prefix.";
|
||||
const expected = {
|
||||
"prefix.foo": "bar",
|
||||
"prefix.baz": "qux",
|
||||
};
|
||||
expect(prefixObjectKeys(obj, prefix)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,14 @@
|
||||
import { DeploymentConfig } from "../types/deploymentConfig";
|
||||
|
||||
const ANNOTATION_PREFIX = "actions.github.com/";
|
||||
|
||||
export function prefixObjectKeys(obj: any, prefix: string): any {
|
||||
return Object.keys(obj).reduce((newObj, key) => {
|
||||
newObj[prefix + key] = obj[key];
|
||||
return newObj;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export function getWorkflowAnnotations(
|
||||
lastSuccessRunSha: string,
|
||||
workflowFilePath: string,
|
||||
@@ -22,7 +31,8 @@ export function getWorkflowAnnotations(
|
||||
helmChartPaths: deploymentConfig.helmChartFilePaths,
|
||||
provider: "GitHub",
|
||||
};
|
||||
return JSON.stringify(annotationObject);
|
||||
const prefixedAnnotationObject = prefixObjectKeys(annotationObject, ANNOTATION_PREFIX);
|
||||
return JSON.stringify(prefixedAnnotationObject);
|
||||
}
|
||||
|
||||
export function getWorkflowAnnotationKeyLabel(
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
weaker than the average human?
|
||||
@@ -0,0 +1,52 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.14.2
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-service
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx-ingress
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||
spec:
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- path: /testpath
|
||||
backend:
|
||||
serviceName: nginx-service
|
||||
servicePort: 80
|
||||
- path: /testpath2
|
||||
backend:
|
||||
serviceName: unrouted-service
|
||||
servicePort: 80
|
||||
@@ -0,0 +1,33 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.14.2
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-service
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
Reference in New Issue
Block a user