Skip to content

Commit

Permalink
feat: basic peforce connection and listing of triggers
Browse files Browse the repository at this point in the history
  • Loading branch information
runreal-warman committed Mar 18, 2024
1 parent 4510bc5 commit 28f2328
Show file tree
Hide file tree
Showing 12 changed files with 593 additions and 0 deletions.
54 changes: 54 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: 'CI'

on:
push:
branches: [main]

pull_request:
branches: [main]

env:
DENO_DIR: ~/.deno-cache

jobs:
ci:
runs-on: ubuntu-latest

strategy:
matrix:
deno-version: [1.41.3]

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Deno ${{ matrix.deno-version }}
uses: denoland/setup-deno@v1
with:
deno-version: ${{ matrix.deno-version }}

- name: Deno Lint
run: deno lint
continue-on-error: true

- name: Deno Format
run: deno fmt --check
continue-on-error: true

- name: Deno Info
run: deno info src/index.ts

- name: Deno Compile [windows]
run: deno task compile-win

- name: Deno Compile [macos]
run: deno task compile-macos

- name: Deno Compile [linux]
run: deno task compile-linux

- name: Cache Deno dependencies
uses: actions/cache@v3
with:
path: ${{ env.DENO_DIR }}
key: ${{ hashFiles('deno.lock') }}
66 changes: 66 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: 'Release'

on:
push:
branches:
- main

permissions:
contents: write
pull-requests: write

env:
DENO_DIR: ~/.deno-cache

jobs:
release:
runs-on: ubuntu-latest

strategy:
matrix:
deno-version: [1.41.3]

steps:
- uses: google-github-actions/release-please-action@v3
id: release
with:
token: ${{ secrets.GH_RUNREAL_TOKEN }}
pull-request-title-pattern: "chore${scope}: release${component} ${version}"
release-type: simple
extra-files: |
README.md
src/version.ts
- uses: actions/checkout@v4
if: ${{ steps.release.outputs.release_created }}

- name: Setup Deno ${{ matrix.deno-version }}
if: ${{ steps.release.outputs.release_created }}
uses: denoland/setup-deno@v1
with:
deno-version: ${{ matrix.deno-version }}

- name: Deno Compile [windows]
if: ${{ steps.release.outputs.release_created }}
run: deno task compile-win

- name: Deno Compile [macos]
if: ${{ steps.release.outputs.release_created }}
run: deno task compile-macos

- name: Deno Compile [linux]
if: ${{ steps.release.outputs.release_created }}
run: deno task compile-linux

- name: Upload Artifacts
if: ${{ steps.release.outputs.release_created }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release upload ${{ steps.release.outputs.tag_name }} ./build/runreal-win-x64.exe ./build/runreal-macos-arm ./build/runreal-linux-x64

- name: Cache Deno dependencies
if: ${{ steps.release.outputs.release_created }}
uses: actions/cache@v3
with:
path: ${{ env.DENO_DIR }}
key: ${{ hashFiles('deno.lock') }}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"deno.enable": true
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Changelog
32 changes: 32 additions & 0 deletions deno.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"imports": {
"/": "./src/",
"./": "./",
"std/": "https://deno.land/[email protected]/"
},
"tasks": {
"start": "deno run -A --watch=src src/index.ts",
"run": "deno run -A src/index.ts",
"compile-win": "deno compile -A --target x86_64-pc-windows-msvc --output build/triggerr-win-x64 src/index.ts",
"compile-linux": "deno compile -A --target x86_64-unknown-linux-gnu --output build/triggerr-linux-x64 src/index.ts",
"compile-macos": "deno compile -A --target aarch64-apple-darwin --output build/triggerr-macos-arm src/index.ts"
},
"lint": {
"include": ["src/"],
"rules": {
"tags": ["recommended"],
"include": ["ban-untagged-todo"],
"exclude": ["no-unused-vars", "no-explicit-any"]
}
},
"fmt": {
"include": ["src/"],
"useTabs": true,
"lineWidth": 120,
"indentWidth": 2,
"singleQuote": true,
"proseWrap": "preserve",
"semiColons": false
}
}

172 changes: 172 additions & 0 deletions deno.lock

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export * as path from 'std/path/mod.ts'
export * as fmt from 'std/fmt/colors.ts'
export * as dotenv from 'std/dotenv/mod.ts'
export * as fs from 'std/fs/mod.ts'
export { mergeReadableStreams } from 'std/streams/merge_readable_streams.ts'

export { Command, EnumType, ValidationError } from 'https://deno.land/x/[email protected]/command/mod.ts'
export { Cell, Row, Table } from 'https://deno.land/x/[email protected]/table/mod.ts'
25 changes: 25 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { VERSION } from './version.ts'
import { Command } from '/deps.ts'
import { Row, Table } from '/deps.ts'
import { PerforceClient } from './lib/p4.ts'

await new Command()
.name('triggerr')
.version(VERSION)
.description('the perforce trigger manager')
.globalOption('-d, --debug', 'Enable debug output.')
.command('list', 'list current triggers')
.action(async () => {
const p4 = new PerforceClient()
const cmd = await p4.runCommandZ(`triggers`, ['-o'])
const triggers = p4.parseTriggersOutput(cmd.output)

new Table()
.header(Row.from(['Name', 'Type', 'Path', 'Command']).border())
.body(
triggers.map((trigger) => new Row(trigger.name, trigger.type, trigger.path, trigger.command)),
)
.border(true)
.render()
})
.parse(Deno.args)
97 changes: 97 additions & 0 deletions src/lib/p4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { exec, ExecResult } from './utils.ts'
import { fs } from '../deps.ts'

type P4Trigger = {
index: number
name: string
type: string
path: string
command: string
}

export class PerforceClient {
private p4Path: string
private cwd: string | URL
private config: Record<string, string> = {}

constructor(p4Path: string = 'p4', cwd: string | URL = Deno.cwd()) {
this.p4Path = p4Path
this.cwd = cwd
this.loadConfig()
}

private loadConfig() {
// Load from a .p4config file first
const configFile = `${this.cwd}/.p4config`
if (fs.existsSync(configFile)) {
const configData = Deno.readTextFileSync(configFile)
configData.split('\n').forEach((line) => {
const [key, value] = line.split('=')
if (key && value) {
this.config[key.trim()] = value.trim()
}
})
}

// Environment variables take precedence, override .p4config values if present
const envConfig = this.loadFromEnvironment()
Object.keys(envConfig).forEach((key) => {
this.config[key] = envConfig[key]
})
}

private loadFromEnvironment(): Record<string, string> {
const config: Record<string, string> = {}
const p4EnvVars = ['P4PORT', 'P4USER', 'P4PASSWD', 'P4CLIENT']
p4EnvVars.forEach((varName) => {
const value = Deno.env.get(varName)
if (value) {
config[varName] = value
}
})
return config
}

runCommand(command: string, args: string[] = [], options: { quiet: boolean } = { quiet: true }): Promise<ExecResult> {
const fullArgs = [command, ...args]
return exec(this.p4Path, fullArgs, {
cwd: this.cwd,
env: this.config,
quiet: options.quiet,
})
}

runCommandZ(
command: string,
args: string[] = [],
options: { quiet: boolean } = { quiet: true },
): Promise<ExecResult> {
const fullArgs = ['-ztag', command, ...args]
return exec(this.p4Path, fullArgs, {
cwd: this.cwd,
env: this.config,
quiet: options.quiet,
})
}

parseTriggersOutput(output: string): P4Trigger[] {
const triggers: P4Trigger[] = []
const lines = output.split('\n')

lines.forEach((line) => {
const match = line.match(/\.\.\. Triggers(\d+) (\S+) (\S+) (\S+) "(.*)"/)
if (match) {
const [, index, name, type, path, command] = match
triggers.push({
index: parseInt(index),
name,
type,
path,
command,
})
}
})

return triggers
}
}
55 changes: 55 additions & 0 deletions src/lib/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Cell, Row, Table } from '../deps.ts'

type P4Trigger = {
index: number
name: string
type: string
path: string
command: string
}

function parseTriggersOutput(output: string): P4Trigger[] {
const triggers: P4Trigger[] = []
const lines = output.split('\n')

lines.forEach((line) => {
// Adjusted regex to match the new input format
const match = line.match(/\.\.\. Triggers(\d+) (\S+) (\S+) (\S+) "(.*)"/)
if (match) {
const [, index, name, type, path, command] = match
triggers.push({
index: parseInt(index),
name,
type,
path,
command,
})
}
})

return triggers
}

// Example usage
const output = `
... Triggers0 slack-notification1 change-commit //Engine/... "C:/Users/ck-user/.deno/bin/deno run --allow-all D:/P4TRIGGERS/p4-notify.ts %user% %changelist%"
... Triggers1 slack-notification2 change-commit //ProjectLyra/... "C:/Users/ck-user/.deno/bin/deno run --allow-all D:/P4TRIGGERS/p4-notify.ts %user% %changelist%"
... Triggers2 buildkite-trigger3 change-commit //Engine/... "C:/Users/ck-user/.deno/bin/deno run --allow-all D:/P4TRIGGERS/bk-trigger.ts buildkite-perforce-test %user% %changelist%"
... Triggers3 buildkite-trigger4 change-commit //ProjectLyra/... "C:/Users/ck-user/.deno/bin/deno run --allow-all D:/P4TRIGGERS/bk-trigger.ts buildkite-perforce-test %user% %changelist%"
`.trim()

const output1 = `
... Triggers0 slack-notification1 change-commit //Engine/... "C:/Users/ck-user/.deno/bin/deno run --allow-all D:/P4TRIGGERS/p4-notify.ts %user% %changelist%"
`.trim()

const triggers = parseTriggersOutput(output)

// console.log(JSON.stringify(triggers, null, 2));

new Table()
.header(Row.from(['Name', 'Type', 'Path', 'Command']).border())
.body(
triggers.map((trigger) => new Row(trigger.name, trigger.type, trigger.path, trigger.command)),
)
.border(true)
.render()
Loading

0 comments on commit 28f2328

Please sign in to comment.