Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dffc480eb9 | |||
| 2358b9fe43 | |||
| 0818f634dc | |||
| ec2f2dbd57 |
@@ -8,6 +8,9 @@ ARG IS_RELEASE="false"
|
|||||||
# Set working directory
|
# Set working directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install git for version generation
|
||||||
|
RUN apk add --no-cache git
|
||||||
|
|
||||||
# Copy package files
|
# Copy package files
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
|
||||||
@@ -53,4 +56,4 @@ ENV LISTEN_ADDR=0.0.0.0
|
|||||||
ENV LISTEN_PORT=3000
|
ENV LISTEN_PORT=3000
|
||||||
|
|
||||||
# Start the integrated server
|
# Start the integrated server
|
||||||
ENTRYPOINT ["./entrypoint.sh"]
|
ENTRYPOINT ["./entrypoint.sh"]
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
services:
|
services:
|
||||||
jmespath-playground:
|
jmespath-playground:
|
||||||
build: .
|
build: .
|
||||||
image: skoszewski/jmespath-playground
|
image: skoszewski/jmespath-playground:latest
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jmespath-playground",
|
"name": "jmespath-playground",
|
||||||
"version": "1.4.1",
|
"version": "1.4.3",
|
||||||
"description": "A React-based web application for testing JMESPath expressions against JSON data",
|
"description": "A React-based web application for testing JMESPath expressions against JSON data",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -36,6 +36,15 @@ function getContainerTool() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isGitRepo() {
|
||||||
|
try {
|
||||||
|
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function generateVersionFile() {
|
async function generateVersionFile() {
|
||||||
const versionModuleUrl = pathToFileURL(path.join(__dirname, 'version.mjs')).href;
|
const versionModuleUrl = pathToFileURL(path.join(__dirname, 'version.mjs')).href;
|
||||||
const { generateVersionFile: generate } = await import(versionModuleUrl);
|
const { generateVersionFile: generate } = await import(versionModuleUrl);
|
||||||
@@ -44,6 +53,26 @@ async function generateVersionFile() {
|
|||||||
return versionFilePath;
|
return versionFilePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function writeVersionFile(version, isRelease) {
|
||||||
|
const versionFilePath = path.join(__dirname, '..', 'src', 'version.js');
|
||||||
|
const contents = [
|
||||||
|
`export const VERSION = '${version}';`,
|
||||||
|
`export const IS_RELEASE = ${isRelease};`,
|
||||||
|
''
|
||||||
|
].join('\n');
|
||||||
|
fs.writeFileSync(versionFilePath, contents, 'utf8');
|
||||||
|
return versionFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readPackageJsonVersion() {
|
||||||
|
const packagePath = path.join(__dirname, '..', 'package.json');
|
||||||
|
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
||||||
|
if (!pkg.version) {
|
||||||
|
throw new Error('package.json does not contain a version');
|
||||||
|
}
|
||||||
|
return pkg.version;
|
||||||
|
}
|
||||||
|
|
||||||
function readVersionFile(versionFilePath) {
|
function readVersionFile(versionFilePath) {
|
||||||
const contents = fs.readFileSync(versionFilePath, 'utf8');
|
const contents = fs.readFileSync(versionFilePath, 'utf8');
|
||||||
const versionMatch = contents.match(/export const VERSION = '([^']+)';/);
|
const versionMatch = contents.match(/export const VERSION = '([^']+)';/);
|
||||||
@@ -79,6 +108,7 @@ Usage:
|
|||||||
Options:
|
Options:
|
||||||
--all-arch Build for both arm64 and amd64 (default: build for host architecture only)
|
--all-arch Build for both arm64 and amd64 (default: build for host architecture only)
|
||||||
--arch <arch> Target architecture (arm64 or amd64). Can be specified multiple times.
|
--arch <arch> Target architecture (arm64 or amd64). Can be specified multiple times.
|
||||||
|
--registry <id> Image registry (default: docker.io). Can also set JMESPATH_REGISTRY.
|
||||||
--help, -h Show this help message and exit
|
--help, -h Show this help message and exit
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
@@ -86,6 +116,7 @@ Examples:
|
|||||||
build-image.mjs --all-arch # Builds for both arm64 and amd64
|
build-image.mjs --all-arch # Builds for both arm64 and amd64
|
||||||
build-image.mjs --arch arm64 # Builds for arm64 only
|
build-image.mjs --arch arm64 # Builds for arm64 only
|
||||||
build-image.mjs --arch arm64 --arch amd64 # Explicitly specify both
|
build-image.mjs --arch arm64 --arch amd64 # Explicitly specify both
|
||||||
|
build-image.mjs --registry docker.io # Use Docker Hub registry explicitly
|
||||||
build-image.mjs -h # Show help`);
|
build-image.mjs -h # Show help`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,6 +136,10 @@ async function main() {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
multiple: true,
|
multiple: true,
|
||||||
description: 'Target architecture (arm64 or amd64)'
|
description: 'Target architecture (arm64 or amd64)'
|
||||||
|
},
|
||||||
|
registry: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Image registry (default: docker.io)'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
strict: true,
|
strict: true,
|
||||||
@@ -117,8 +152,19 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const containerTool = getContainerTool();
|
const containerTool = getContainerTool();
|
||||||
const versionFilePath = await generateVersionFile();
|
let version;
|
||||||
const { version, isRelease } = readVersionFile(versionFilePath);
|
let isRelease;
|
||||||
|
|
||||||
|
if (isGitRepo()) {
|
||||||
|
const versionFilePath = await generateVersionFile();
|
||||||
|
const versionInfo = readVersionFile(versionFilePath);
|
||||||
|
version = versionInfo.version;
|
||||||
|
isRelease = versionInfo.isRelease;
|
||||||
|
} else {
|
||||||
|
version = readPackageJsonVersion();
|
||||||
|
isRelease = true;
|
||||||
|
writeVersionFile(version, isRelease);
|
||||||
|
}
|
||||||
|
|
||||||
let architectures;
|
let architectures;
|
||||||
if (values['all-arch']) {
|
if (values['all-arch']) {
|
||||||
@@ -133,14 +179,16 @@ async function main() {
|
|||||||
console.log(`Target architectures: ${architectures.join(', ')}`);
|
console.log(`Target architectures: ${architectures.join(', ')}`);
|
||||||
|
|
||||||
// Build container image
|
// Build container image
|
||||||
|
const registry = values.registry || process.env.JMESPATH_REGISTRY || 'docker.io';
|
||||||
|
const imageName = `${registry.replace(/\/$/, '')}/skoszewski/jmespath-playground`;
|
||||||
const tags = isRelease
|
const tags = isRelease
|
||||||
? [
|
? [
|
||||||
`-t skoszewski/jmespath-playground:${version}`,
|
`-t ${imageName}:${version}`,
|
||||||
`-t skoszewski/jmespath-playground:latest`
|
`-t ${imageName}:latest`
|
||||||
].join(' ')
|
].join(' ')
|
||||||
: [
|
: [
|
||||||
`-t skoszewski/jmespath-playground:dev`,
|
`-t ${imageName}:dev`,
|
||||||
`-t skoszewski/jmespath-playground:latest`
|
`-t ${imageName}:latest`
|
||||||
].join(' ');
|
].join(' ');
|
||||||
|
|
||||||
const archFlags = architectures.map(arch => `--arch ${arch}`).join(' ');
|
const archFlags = architectures.map(arch => `--arch ${arch}`).join(' ');
|
||||||
@@ -160,15 +208,15 @@ async function main() {
|
|||||||
|
|
||||||
if (isRelease) {
|
if (isRelease) {
|
||||||
console.log(`\nTo run the container:`);
|
console.log(`\nTo run the container:`);
|
||||||
console.log(` ${containerTool} run --arch arm64 --name jmespathpg -p 3000:3000 skoszewski/jmespath-playground:${version}`);
|
console.log(` ${containerTool} run --arch arm64 --name jmespathpg -p 3000:3000 ${imageName}:${version}`);
|
||||||
if (containerTool === 'docker') {
|
if (containerTool === 'docker') {
|
||||||
console.log(`\nTo push to Docker Hub:`);
|
console.log(`\nTo push to Docker Hub:`);
|
||||||
console.log(` docker push skoszewski/jmespath-playground:${version}`);
|
console.log(` docker push ${imageName}:${version}`);
|
||||||
console.log(` docker push skoszewski/jmespath-playground:latest`);
|
console.log(` docker push ${imageName}:latest`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`\nTo run the container:`);
|
console.log(`\nTo run the container:`);
|
||||||
console.log(` ${containerTool} run --arch arm64 --name jmespathpg -p 3000:3000 skoszewski/jmespath-playground:dev`);
|
console.log(` ${containerTool} run --arch arm64 --name jmespathpg -p 3000:3000 ${imageName}:dev`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,24 @@ import { readFileSync, write, writeFileSync } from "fs";
|
|||||||
import { execSync } from "child_process";
|
import { execSync } from "child_process";
|
||||||
import semver from "semver";
|
import semver from "semver";
|
||||||
|
|
||||||
|
export function isGitAvailable() {
|
||||||
|
try {
|
||||||
|
execSync("git --version", { stdio: "ignore" });
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isGitRepo() {
|
||||||
|
try {
|
||||||
|
execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore" });
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getGitVersion() {
|
export function getGitVersion() {
|
||||||
let rawGitVersion;
|
let rawGitVersion;
|
||||||
let gitVersion;
|
let gitVersion;
|
||||||
@@ -28,26 +46,38 @@ export function getGitVersion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function generateVersionFile(versionFilePath) {
|
export function generateVersionFile(versionFilePath) {
|
||||||
|
if (!isGitAvailable()) {
|
||||||
|
throw new Error("Git is required to generate version info.");
|
||||||
|
}
|
||||||
|
|
||||||
// Read package.json version
|
// Read package.json version
|
||||||
const packageVersion = JSON.parse(
|
const packageVersion = JSON.parse(
|
||||||
readFileSync("package.json", { encoding: "utf-8" }),
|
readFileSync("package.json", { encoding: "utf-8" }),
|
||||||
).version;
|
).version;
|
||||||
// Get version from git repository
|
let gitVersion = packageVersion;
|
||||||
const gitVersion = getGitVersion();
|
let gitBaseVersion = packageVersion;
|
||||||
const gitBaseVersion = semver.coerce(gitVersion)?.version;
|
let isRelease = true;
|
||||||
|
|
||||||
// if git returned malformed version, throw error
|
if (isGitRepo()) {
|
||||||
if (!gitBaseVersion || gitBaseVersion === "0.0.0") {
|
// Get version from git repository
|
||||||
throw new Error(
|
gitVersion = getGitVersion();
|
||||||
"Cannot determine git version. Make sure the script is run in a git repository with tags.",
|
gitBaseVersion = semver.coerce(gitVersion)?.version;
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare git version with package.json version
|
// if git returned malformed version, throw error
|
||||||
if (semver.neq(gitBaseVersion, packageVersion)) {
|
if (!gitBaseVersion || gitBaseVersion === "0.0.0") {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Version mismatch: package.json version is ${packageVersion}, but git version is ${gitBaseVersion}`,
|
"Cannot determine git version. Make sure the script is run in a git repository with tags.",
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare git version with package.json version
|
||||||
|
if (semver.neq(gitBaseVersion, packageVersion)) {
|
||||||
|
throw new Error(
|
||||||
|
`Version mismatch: package.json version is ${packageVersion}, but git version is ${gitBaseVersion}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
isRelease = gitVersion === packageVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate version file
|
// Generate version file
|
||||||
@@ -58,7 +88,7 @@ export function generateVersionFile(versionFilePath) {
|
|||||||
// Generated at: ${buildDate}
|
// Generated at: ${buildDate}
|
||||||
|
|
||||||
export const VERSION = '${packageVersion}';
|
export const VERSION = '${packageVersion}';
|
||||||
export const IS_RELEASE = ${gitVersion === packageVersion};
|
export const IS_RELEASE = ${isRelease};
|
||||||
export const BUILD_TIME = '${buildDate}';
|
export const BUILD_TIME = '${buildDate}';
|
||||||
`,
|
`,
|
||||||
);
|
);
|
||||||
|
|||||||
7
terraform/.gitignore
vendored
Normal file
7
terraform/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Terraform
|
||||||
|
*.tfstate
|
||||||
|
*.tfstate.*
|
||||||
|
.terraform/
|
||||||
|
.terraform.lock.hcl
|
||||||
|
.terraform.lock.hcl.backup
|
||||||
|
*tfplan
|
||||||
41
terraform/main.tf
Normal file
41
terraform/main.tf
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Create Cloud Run service
|
||||||
|
resource "google_cloud_run_v2_service" "jppg" {
|
||||||
|
name = "jmespath-playground"
|
||||||
|
location = var.default_region
|
||||||
|
invoker_iam_disabled = true
|
||||||
|
|
||||||
|
template {
|
||||||
|
containers {
|
||||||
|
image = "skoszewski/jmespath-playground:${var.image_version}"
|
||||||
|
name = "jmespath-playground-1"
|
||||||
|
ports {
|
||||||
|
container_port = 3000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scaling {
|
||||||
|
max_instance_count = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
deletion_protection = var.deletion_protection
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
ignore_changes = [
|
||||||
|
client
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_cloud_run_domain_mapping" "jppg" {
|
||||||
|
name = "jmespath-playground.gcp-lab.koszewscy.waw.pl"
|
||||||
|
location = var.default_region
|
||||||
|
|
||||||
|
spec {
|
||||||
|
route_name = google_cloud_run_v2_service.jppg.name
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
namespace = var.project_id
|
||||||
|
}
|
||||||
|
}
|
||||||
13
terraform/providers.tf
Normal file
13
terraform/providers.tf
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
google = {
|
||||||
|
source = "hashicorp/google"
|
||||||
|
version = "~>7.17.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "google" {
|
||||||
|
project = var.project_id
|
||||||
|
region = var.default_region
|
||||||
|
}
|
||||||
5
terraform/terraform.tfvars.json
Normal file
5
terraform/terraform.tfvars.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"project_id": "dom-lab",
|
||||||
|
"image_version": "1.4.3",
|
||||||
|
"deletion_protection": true
|
||||||
|
}
|
||||||
21
terraform/variables.tf
Normal file
21
terraform/variables.tf
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
variable "project_id" {
|
||||||
|
description = "Google project id"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "default_region" {
|
||||||
|
description = "Default Google Cloud region"
|
||||||
|
type = string
|
||||||
|
default = "europe-west1" # Belgium
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "image_version" {
|
||||||
|
description = "Version of the Docker image"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "deletion_protection" {
|
||||||
|
type = bool
|
||||||
|
description = "Protect resources from deletion using terraform destroy and apply."
|
||||||
|
default = true
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user