Skip to content

Commit

Permalink
migrate to js action
Browse files Browse the repository at this point in the history
  • Loading branch information
GregoireW committed Oct 15, 2024
1 parent 1531de4 commit 3604541
Show file tree
Hide file tree
Showing 6 changed files with 950 additions and 72 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
out/
/node_modules/
94 changes: 22 additions & 72 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,89 +9,39 @@ inputs:
description: "path where an initial script is."
required: false
default: ""
config-file:
description: "path to the dblinter configuration file"
required: false
default: ""

report-path:
description: "Path to write the sarif report file"
required: true
default: "dblinter.sarif"

dblinter-version:
description: "dblinter version"
required: false
default: "latest"

postgres-version:
description: "postgres version to use"
required: false
default: "17"
flyway-version:
description: "flyway version to use"
required: false
default: "10"

pr-comment:
description: "Display the report in the PR"
required: false
default: "false"
GITHUB_TOKEN:
description: 'Github token of the repository (automatically created by Github) to create the PR comment'
default: ${{ github.token }}
required: false

outputs:
sarif-report:
description: "sarif where the report is stored"
value: ${{ steps.dblinter.outputs.location }}

runs:
using: "composite"
steps:
- name: Launch postgres database
id: launch
shell: bash
run: |
PGPASS=$(openssl rand -base64 12 | sed s{/{_{g )
echo "::add-mask::$PGPASS"
docker pull -q postgres:16
PG_CONTAINER=$(docker run -d -e POSTGRES_PASSWORD=$PGPASS postgres:16)
echo "pg_container=$PG_CONTAINER" >> $GITHUB_OUTPUT
PG_HOST=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $PG_CONTAINER)
echo "PGHOST=$PG_HOST" >> $GITHUB_ENV
echo "PGPORT=5432" >> $GITHUB_ENV
echo "PGUSER=postgres" >> $GITHUB_ENV
echo "PGPASSWORD=$PGPASS" >> $GITHUB_ENV
echo "PGDATABASE=postgres" >> $GITHUB_ENV
- name: Execute flyway
if: inputs.flyway-migration != ''
shell: bash
run: |
ABSOLUTE_FLYWAY_MIGRATION=$(realpath ${{ inputs.flyway-migration }})
docker pull -q flyway/flyway:10
docker run --rm -v $ABSOLUTE_FLYWAY_MIGRATION:/flyway/sql flyway/flyway:10 -locations="filesystem:/flyway/sql" -url=jdbc:postgresql://$PGHOST:$PGPORT/$PGDATABASE -user=$PGUSER -password=$PGPASSWORD migrate
- name: Execute init script
if: inputs.init-script != ''
shell: bash
run: |
psql -v ON_ERROR_STOP=1 -f ${{ inputs.init-script }} > /dev/null
- name: get dblinter
shell: bash
run: |
docker pull -q decathlon/dblinter:${{inputs.dblinter-version}}
- name: Run dblinter
id: dblinter
shell: bash
run: |
mkdir -p $(dirname "${{ inputs.report-path }}")
ABSOLUTE_OUTPUT=$(realpath "${{ inputs.report-path }}")
echo "location=$ABSOLUTE_OUTPUT" >> $GITHUB_OUTPUT
ABSOLUTE_OUTPUT_DIR=$(dirname "$ABSOLUTE_OUTPUT")
FILENAME=$(basename "$ABSOLUTE_OUTPUT")
if [ -f "${{ inputs.config-file}}" ] ; then
ABSOLUTE_CONFIG=$(realpath "${{ inputs.config-file }}")
ABSOLUTE_CONFIG_DIR=$(dirname "$ABSOLUTE_CONFIG")
CONFIG_FILENAME=$(basename "$ABSOLUTE_CONFIG")
DOCKER_PARAMS="-v \"$ABSOLUTE_CONFIG_DIR:/config\""
CONFIG_FILE="-f \"/config/$CONFIG_FILENAME\""
fi
echo "----------------------------------------------------------------------"
echo "-- Running dblinter now --"
echo "----------------------------------------------------------------------"
docker run --rm -t -u $(id -u) -v "$ABSOLUTE_OUTPUT_DIR:/report" $DOCKER_PARAMS decathlon/dblinter:${{inputs.dblinter-version}} --dbname $PGDATABASE --host $PGHOST --user $PGUSER --password $PGPASSWORD --port $PGPORT -o "/report/$FILENAME" $CONFIG_FILE
echo "----------------------------------------------------------------------"
echo "-- Dblinter scan finished --"
echo "----------------------------------------------------------------------"
- name: clean up
if: always()
shell: bash
run: |
docker stop ${{ steps.launch.outputs.pg_container }}
docker rm ${{ steps.launch.outputs.pg_container }}
using: "node20"
main: "dist/index.js"
229 changes: 229 additions & 0 deletions dblinter-report.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
const fs = require('fs');
const core = require('@actions/core');
const exec = require('@actions/exec');
const github = require('@actions/github');
const docker = require('docker-cli-js');

function buildReport(reportPath) {
const actualContent = JSON.parse(fs.readFileSync(reportPath, "utf-8"));

let report = "# DBLinter Report:\n\n";

if (actualContent.runs[0].results && actualContent.runs[0].results.length > 0) {
report += "## Issues found:\n";
report += "```diff\n";
actualContent.runs[0].results.forEach(r => {
report += `- ⚠️ ${r.ruleId} ${r.message.text}\n`
report += `+ ↪️ ${r.fixes}\n\n`
});
report += "```\n";
} else {
report += "No issues found";
}
return report;
}

async function createComment(report) {
const context = github.context;

let comment;
for await (const {data: comments} of octokit.paginate.iterator(octokit.rest.issues.listComments, {
...context.repo,
issue_number,
})) {
comment = comments.find((comment) => comment?.body?.includes("# DBLinter Report:"));
if (comment) break;
}

if (comment) {
await octokit.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: comment.id,
body: report
});
} else {
await octokit.rest.issues.createComment({
issue_number: context.payload.pull_request?.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: report
})
}
}


function validateInputForExec(input) {
const value = core.getInput(input);
if (!value) {
core.setFailed(`${input} is required`);
exit(1);
}
const regex = /^[a-zA-Z0-9.-_]+$/;
if(!regex.test(input)) {
core.setFailed(`${input} should only contain alphanumeric characters, dot, hyphens, underscores`);
exit(1);
}

return value;
}

function validateInput(){
const reportPath = fs.realpathSync(core.getInput('report-path'));
// Remove last part of report path
const reportDir = reportPath.split('/').slice(0, -1).join('/');
if (!fs.existsSync(reportDir)) {
fs.mkdirSync(reportDir, { recursive: true });
}
if (!fs.existsSync(reportPath)) {
core.setFailed(`Report file not found: ${reportPath}`);
}

let flywayMigration = core.getInput('flyway-migration');
if (flywayMigration) {
flywayMigration = fs.realpathSync(flywayMigration);
if (!fs.existsSync(flywayMigration)) {
core.setFailed(`Flyway migration file not found: ${flywayMigration}`);
exit(1);
}
}

let initScript = core.getInput('init-script');
if (initScript) {
initScript = fs.realpathSync(initScript);
if (!fs.existsSync(initScript)) {
core.setFailed(`Init script file not found: ${initScript}`);
exit(1);
}
}

const dblinterVersion = validateInputForExec("dblinter-version");
const postgresVersion = validateInputForExec("postgres-version");
const flywayVersion = validateInputForExec("flyway-version");

const prComment = core.getInput('pr-comment')==='true';
const githubToken = core.getInput('GITHUB_TOKEN');
if ( (github.context.eventName.toLowerCase()==='pull_request') && prComment && !githubToken) {
core.setFailed("GITHUB_TOKEN is required to create a PR comment");
exit(1);
}



return {
reportPath,
reportDir,
flywayMigration,
initScript,
dblinterVersion,
postgresVersion,
flywayVersion,
prComment,
githubToken,
};
}

async function downloadDockerImage(config){
docker.dockerCommand('pull decathlon/dblinter:'+config.dblinterVersion);
docker.dockerCommand('pull flyway/flyway:'+config.flywayVersion);
await docker.dockerCommand('pull postgres:'+config.postgresVersion);
}


async function launchPostgres(config) {
const pgPass = Buffer.from(crypto.randomBytes(12)).toString('base64').replace(/\//g, '_');
core.setSecret(pgPass);

const container=await docker.dockerCommand(`run -d -e POSTGRES_PASSWORD=${pgPass} postgres:${config.postgresVersion}`);
console.log("------------ container ------------");
console.log(container);
console.log("------------ /container ------------");

const inspect= await docker.dockerCommand(`inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ${container.containerId}`);
console.log("------------ inspect ------------");
console.log(inspect);
console.log("------------ /inspect ------------");

return {
pgContainer: container.containerId,
pgHost: inspect.raw,
pgPort: 5432,
pgUser: 'postgres',
pgPass,
pgDatabase: 'postgres',
};

}

async function executeFlyway(config, postgres) {
if (!config.flywayMigration) {
return;
}
await docker.dockerCommand(`run --rm -v ${config.flywayMigration}:/flyway/sql flyway/flyway:${config.flywayVersion} -locations="filesystem:/flyway/sql" -url=jdbc:postgresql://${postgres.pgHost}:${postgres.pgPort}/${postgres.pgDatabase} -user=${postgres.pgUser} -password=${postgres.pgPass} migrate`, {stdio: 'inherit'});
}

async function executeInitSql(config, postgres){
if (!config.initScript) {
return;
}

const exitCode = await exec.exec("psql",
["-v","ON_ERROR_STOP=1", "-f", config.initScript],
{env: {
PGPASSWORD: postgres.pgPass,
PGHOST: postgres.pgHost,
PGPORT: postgres.pgPort,
PGUSER: postgres.pgUser,
PGDATABASE: postgres.pgDatabase
}});

if (exitCode !== 0) {
core.setFailed("Error executing init script");
exit(1);
}
}


async function executeDblinter(options, postgres) {
console.log("----------------------------------------------------------------------");
console.log("-- Running dblinter now --");
console.log("----------------------------------------------------------------------");
docker.dockerCommand("run --rm -t -u $(id -u) -v $ABSOLUTE_OUTPUT_DIR:/report decathlon/dblinter:${{inputs.dblinter-version}} --dbname $PGDATABASE --host $PGHOST --user $PGUSER --password $PGPASSWORD --port $PGPORT -o /report/$FILENAME", {stdio: 'inherit'});
console.log("----------------------------------------------------------------------");
console.log("-- Dblinter scan finished --");
console.log("----------------------------------------------------------------------");
}


async function main() {
const options = validateInput();
await downloadDockerImage(options);
const postgres = await launchPostgres(options);

await executeFlyway(options, postgres);
await executeInitSql(options, postgres);


await executeDblinter(options, postgres);

core.setOutput("sarif-report", options.reportPath);

docker.dockerCommand(`kill ${postgres.pgContainer}`);

const report = buildReport(options.reportPath);

const github_token = core.getInput('GITHUB_TOKEN');
const octokit = github.getOctokit(github_token);

const context = github.context;
const issue_number = context.payload.pull_request?.number;

if (!issue_number) {
core.info('No issue number found.');
} else {
createComment(report);
}
}


main();
11 changes: 11 additions & 0 deletions dist/index.js

Large diffs are not rendered by default.

Loading

0 comments on commit 3604541

Please sign in to comment.