Skip to content

Commit

Permalink
Add support for hemfile (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
dragonraid authored Dec 12, 2020
1 parent cfde8cd commit bdccf70
Show file tree
Hide file tree
Showing 15 changed files with 321 additions and 188 deletions.
7 changes: 7 additions & 0 deletions .dockeringnore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules
.env*
action.yml
LICENSE
README.md
.gitignore
.eslintrc.yml
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
test.sh
node_modules
.env
.env*
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM node:14

WORKDIR /app

ARG HELMFILE_VERSION=v0.135.0

ADD https://github.com/roboll/helmfile/releases/download/${HELMFILE_VERSION}/helmfile_linux_amd64 \
/tmp/

COPY . .

RUN npm install \
&& mv /tmp/helmfile_linux_amd64 /usr/local/bin/helmfile \
&& chmod +x /usr/local/bin/helmfile \
&& curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash

ENTRYPOINT [ "node", "/app/src/index.js" ]
53 changes: 36 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ It does so by reading specified file in your repository updating respective
dependency/version and opening pull request with this update to against default branch.
See _Types_ section to see, what can this action update.

> NOTE: This action is not using standard github action execution with `uses` keyword, because that
> would require to check in node_modules. Instead, it clones this action and then runs npm install
> and npm start
## Inputs

Environment variables are used for inputs instead of actual github action inputs,
Expand Down Expand Up @@ -55,7 +51,7 @@ By default this type returns latest image based on your inputs:
| INSTANCE_TYPE | Virtualization details, varies based on cloud | `hvm-ssd` | no |
| RELEASE | release (do not supply if you want latest) | `20200924` | no |

## Example
#### Example

```yaml
name: update ubuntu base image
Expand All @@ -68,17 +64,11 @@ jobs:
update:
runs-on: ubuntu-20.04
steps:
- name: checkout dragonraid/deployment-bumper
uses: actions/checkout@v2
with:
repository: dragonraid/deployment-bumper
ref: refs/heads/main
path: ./.github/actions/deployment-bumper
- name: run deployment-bumper
working-directory: ./.github/actions/deployment-bumper
- name: update ubuntu AMI
uses: dragonraid/deployment-bumper
env:
TYPE: ubuntu
FILE: packer.json
FILE: ami.json
REPOSITORY: dragonraid/test
USERNAME: ${{ secrets.username }}
PASSWORD: ${{ secrets.password }}
Expand All @@ -88,7 +78,36 @@ jobs:
VERSION: '20.04'
ARCHITECTURE: amd64
INSTANCE_TYPE: hvm-ssd
run: |
npm install --only=prod
npm start
```

### Helmfile lock

With this type you can update [helmfile](https://github.com/roboll/helmfile) lock files.
Under the hood this type runs [helmfile deps](https://github.com/roboll/helmfile#deps) command.

| Input | Description | Example | Required |
| :---------- | --------------------------------------: | ------: | -------: |
| ENVIRONMENT | helmfile global options `--environment` | `myEnv` | no |

#### Example

```yaml
name: update helmfile lock
on:
schedule:
- cron: '0 0 1 * *'
jobs:
update:
runs-on: ubuntu-20.04
steps:
- name: update helmfile.lock
uses: dragonraid/deployment-bumper
env:
TYPE: helmfile
FILE: helmfile.yaml
USERNAME: ${{ secrets.username }}
PASSWORD: ${{ secrets.password }}
ENVIRONMENT: staging
```
3 changes: 2 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
name: 'Deployment Bumper'
description: 'Update deployment related stuff'
runs:
using: 'node12'
using: 'docker'
image: 'Dockerfile'
branding:
icon: 'edit'
color: blue
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
"main": "src/index.js",
"scripts": {
"cleanup": "rm -fr /tmp/dragonraid",
"dev": "npm run cleanup && node src/index.js",
"start": "NODE_ENV=production node src/index.js",
"test": "jest ."
"dev-ubuntu": "npm run cleanup && DOTENV_CONFIG_PATH='./.envUbuntu' node -r dotenv/config src/index.js",
"dev-helmfile": "npm run cleanup && DOTENV_CONFIG_PATH='./.envHelmfile' node -r dotenv/config src/index.js",
"start": "node src/index.js",
"test": "jest .",
"lint": "eslint ."
},
"author": {
"name": "Lukas Novotny",
Expand Down
4 changes: 2 additions & 2 deletions src/github.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ class Repository {
* @param {string} username - github username
* @param {string} password - github personal access token
* @param {string} path - where to clone repository
* @param {string} branchNamePrefix - branch name prefix
* @param {string} branchName - branch name prefix
* @param {string} pathPrefix - local repository path prefix
*/
constructor({
repository,
username,
password,
path,
username,
branchName,
pathPrefix = '/tmp/',
}) {
Expand Down
56 changes: 56 additions & 0 deletions src/handlers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const { Helmfile } = require('./processors/helmfile');
const { Ubuntu } = require('./processors/ubuntu');

/**
* Ubuntu processor handler. Edits specified value in specified file
* based on input parameters
* @param {object} filePath - full path to file
*/
const handleUbuntu = async (filePath) => {
const filterValues = {
cloud: process.env.CLOUD || null,
zone: process.env.ZONE || null,
version: process.env.VERSION || null,
architecture: process.env.ARCHITECTURE || null,
instanceType: process.env.INSTANCE_TYPE || null,
release: process.env.RELEASE || null,
};

const rawKeys = process.env.KEYS;
let keys;
if (rawKeys) {
keys = rawKeys.split(',');
} else {
throw new Error('KEYS must be supplied!');
}

const filter = {};
Object.keys(filterValues).forEach((value) => {
if (filterValues[value]) filter[value] = filterValues[value];
});
const ubuntu = new Ubuntu(filter, filePath, keys);
await ubuntu.run();
console.log(`File "${filePath}" has been successfully updated.`);
};

/**
* Helmfile processor handler. Updates helmfiles lock file.
* @param {object} filePath - full path to file
*/
const handleHelmfile = async (filePath) => {
const helmfileArgs = {
file: filePath,
};
if (process.env.ENVIRONMENT) {
helmfileArgs.environment = process.env.ENVIRONMENT;
}

const helmfile = new Helmfile(helmfileArgs);
await helmfile.run();
console.log(`File "${filePath}" has been successfully updated.`);
};

module.exports = {
handleUbuntu,
handleHelmfile,
};
112 changes: 32 additions & 80 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
const { File } = require('./file');
const { Repository } = require('./github');
const { Ubuntu } = require('./ubuntu');
const handlers = require('./handlers');

// load environment variables from .env file if not running in production
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config();
}

// Check if environment variable exists, otherwise set default
/**
* Raw configuration.
*/
const RAW_CONFIG = {
TYPE: process.env.TYPE || null,
FILE: process.env.FILE || null,
KEYS: process.env.KEYS || null,
BRANCH_NAME: process.env.BRANCH_NAME || process.env.TYPE,
BRANCH_PREFIX: process.env.BRANCH_PREFIX || 'update',
REPOSITORY: process.env.REPOSITORY || process.env.GITHUB_REPOSITORY,
Expand All @@ -30,45 +25,22 @@ const CONFIG = {};
const processConfig = () => {
for (const [key, value] of Object.entries(RAW_CONFIG)) {
if (!value) {
if (value === 'USERNAME' || value === 'PASSWORD') continue;
if (key === 'USERNAME' || key === 'PASSWORD') {
throw new Error(`Invalid USERNAME or PASSWORD!`);
};
throw new Error(
`Invalid configuration value: ${value} for ${key}.`,
);
}
switch (key) {
case 'KEYS':
CONFIG[key] = value.split(',');
break;
default:
CONFIG[key] = value;
}
CONFIG[key] = value;
}
};

const handleUbuntu = async () => {
const filterValues = {
cloud: process.env.CLOUD || null,
zone: process.env.ZONE || null,
version: process.env.VERSION || null,
architecture: process.env.ARCHITECTURE || null,
instanceType: process.env.INSTANCE_TYPE || null,
release: process.env.RELEASE || null,
};

const filter = {};
Object.keys(filterValues).forEach((value) => {
if (filterValues[value]) filter[value] = filterValues[value];
});
const ubuntu = new Ubuntu(filter);
const latestUbuntu = await ubuntu.latest;
const keyValuePairs = {};
CONFIG.KEYS.forEach((key) => {
keyValuePairs[key] = latestUbuntu.id;
});

return keyValuePairs;
};

/**
* Clone repository and checkout the feature branch
* @param {string} branchName - feature branch name
* @return {object}
*/
const initializeRepo = async (branchName) => {
const repository = new Repository({
repository: CONFIG.REPOSITORY,
Expand All @@ -81,57 +53,37 @@ const initializeRepo = async (branchName) => {
return repository;
};

const throwError = (resolved) => {
resolved.forEach((process) => {
if (process.status === 'rejected') {
console.error('An error has occurred.');
throw new Error(JSON.stringify(process.reason));
}
});
};

const TYPE_PROCESSORS = {
ubuntu: handleUbuntu,
/**
* Define handle types
*/
const PROCESSOR_TYPES = {
ubuntu: handlers.handleUbuntu,
helmfile: handlers.handleHelmfile,
};

const main = async () => {
/**
* Main function
*/
(async () => {
try {
processConfig();
} catch (err) {
console.error('Config processor has failed!', err);
process.exit(1);
}
const processor = await Promise.allSettled([
TYPE_PROCESSORS[CONFIG.TYPE](),
// TODO: option for supplying custom branch name and custom prefix
initializeRepo(`${CONFIG.BRANCH_PREFIX}/${CONFIG.BRANCH_NAME}`),
]);
throwError(processor);
console.log(`Successfully executed processor ${CONFIG.TYPE}.`);

const repository = processor[1].value;
const filePath = `${repository.path}/${CONFIG.FILE}`;
const keyValuePairs = processor[0].value;

try {
const file = new File({ filePath, keyValuePairs });
await file.run();
} catch (err) {
console.error('Updating file has failed!', err);
process.exit(1);
}
console.log(`File "${filePath}" has been successfully updated.`);

try {
const repository = await initializeRepo(
`${CONFIG.BRANCH_PREFIX}/${CONFIG.BRANCH_NAME}`,
);
const fullFilePath = `${repository.path}/${CONFIG.FILE}`;
await PROCESSOR_TYPES[CONFIG.TYPE](fullFilePath);
console.log(`Successfully executed processor ${CONFIG.TYPE}.`);
await repository.push(`update ${CONFIG.TYPE}`);
await repository.createPullRequest();
console.log('Pull-request created.');
} catch (err) {
console.error(`Opening pull request with changes has failed!`, err);
console.error(`${CONFIG.TYPE} processor failed.`, err);
process.exit(1);
}
console.log(
`Successfully pushed branch "${repository.branchName}" to remote.`,
);
};

main();
})();
Loading

0 comments on commit bdccf70

Please sign in to comment.