6 Commits
Author SHA1 Message Date
james.maes e41534a47e Merge pull request 'ci: use shared ci-workflows Maven workflow' (#74) from ci/ci-workflows into develop
maven / build (push) Successful in 1m38s
build / maven (push) Successful in 1m38s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 539bec410f7926fec3ef7fabe51934a91e0248be] (push) Successful in 11s
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: native / native-result (push) Successful in 8s
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (push) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: ci / node-8 (push) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-business (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / original-voyage (push) Skipped
QRun-IO/ci-policy: ci / original-cli (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (push) Skipped
QRun-IO/ci-policy: ci / ci-result (push) Successful in 8s
2026-11-06 18:02:24 +00:00
james.maesandClaude Opus 5.5 0a35966498 ci: use shared ci-workflows Maven workflow
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 6s
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (pull_request) Successful in 7s
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI with Docker / secrets (pull_request) Successful in 16s
maven / build (push) Successful in 1m23s
build / maven (push) Successful in 1m23s
QRun-IO/ci-policy: Shared Java Maven CI with Docker / verify (pull_request) Successful in 1m29s
QRun-IO/ci-policy: Shared Java Maven CI with Docker / gate (pull_request) Successful in 11s
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Successful in 42m28s
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 4s
Replace the platform-ci release callers with one caller of
KofTwentyTwo/ci-workflows maven.yaml (qqq-orb port).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 15:51:51 +00:00
james.maesandCodex 6a27f3d9b4 ci: align canonical shared orb workers (#69)
Co-authored-by: Codex <noreply@openai.com>
2026-10-21 01:33:03 -05:00
james.maesandCodex f80d1ac9f8 ci: delegate pipelines to shared QQQ orb
Co-Authored-By: Codex <noreply@openai.com>
2026-10-20 19:39:50 -05:00
James MaesandClaude Opus 4.8 e47cca4b81 docs: add CLAUDE.md pointer to QQQ knowledge-base dossier + hub
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 05:31:35 -05:00
james.maes c7cf55a99e chore: migrate license from AGPL-3.0 to Apache-2.0 2026-01-05 10:11:44 -06:00
10 changed files with 28 additions and 184 deletions

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
name: build
on:
push:
jobs:
maven:
uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/maven.yaml@v1
secrets: inherit
with:
publish: false
-31
View File
@@ -1,31 +0,0 @@
# GitHub Release Configuration
# Auto-generates release notes from PR labels
changelog:
exclude:
labels:
- ignore-for-release
- duplicate
- invalid
authors:
- dependabot
- dependabot[bot]
categories:
- title: Breaking Changes
labels:
- breaking-change
- breaking
- title: New Features
labels:
- enhancement
- feature
- title: Bug Fixes
labels:
- bug
- fix
- title: Dependencies
labels:
- dependencies
- title: Other Changes
labels:
- "*"
+8
View File
@@ -189,3 +189,11 @@ mvn test -pl qctl-integration-tests # E2E tests only
## Session Continuity
To continue from last session, see `docs/internal/session-state.md` for current progress and next steps.
## Knowledge base
Reviewed dossier for this repo (commit `c7cf55a99e51`, branch `develop`, 2026-07-04):
`R:/Git.Local/KofTwentyTwo/second-brain/knowledge/qqq/repos/qctl.md`
QQQ platform hub (cross-repo map, v4.0 impact tracking, licensing follow-ups):
`R:/Git.Local/KofTwentyTwo/second-brain/knowledge/qqq/qqq-hub.md`
+1 -17
View File
@@ -1,22 +1,6 @@
# Session State
Last updated: 2026-10-30
## Native CI qualification fixes (2026-10-30)
Local native CI exposed three defects: `--no-prompt` omitted declared defaults,
unknown templates still entered interactive selection, and Windows backslashes
escaped Velocity path variables. Defaults now apply with validated explicit
overrides; unknown templates return NOT_FOUND without reading stdin; path
separators are normalized before interpolation. Interactive fallback remains
covered separately.
Four non-interactive regressions and a Windows-path regression failed before the
fixes. On macOS27.2 ARM64/GraalVM21.0.12, full Maven verification, native packaging
and verification with the built binary passed235 tests across26 reports with
zero failures/errors/skips. Native tests must run after building the binary;
the earlier build-before-test arrangement silently skipped10 native E2E cases.
Exact-commit Gitea macOS/Windows verification and normal PR review remain pending.
Last updated: 2026-01-03
## Current Status
@@ -298,82 +298,6 @@ class InitCommandE2ETest
/***************************************************************************
* Non-interactive generation applies defaults without consuming stdin.
*
* @throws IOException if the generated file cannot be read
* @since 0.2.1
***************************************************************************/
@Test
void testInit_noPrompt_usesDefaults() throws IOException
{
Path outputDir = tempDir.resolve("default-values");
harness.execute("qqq", "init", "test-with-prompts", "--no-prompt",
"-o", outputDir.toString());
assertThat(harness.getExitCode()).as(harness.getStderr()).isEqualTo(ExitCodes.SUCCESS);
assertThat(Files.readString(outputDir.resolve("README.md")))
.contains("my-project", "com.example");
}
/***************************************************************************
* Explicit variables override defaults while omitted variables retain them.
*
* @throws IOException if the generated file cannot be read
* @since 0.2.1
***************************************************************************/
@Test
void testInit_noPrompt_overridesOneDefault() throws IOException
{
Path outputDir = tempDir.resolve("override-default");
harness.execute("qqq", "init", "test-with-prompts", "--no-prompt",
"--var", "projectName=custom-project", "-o", outputDir.toString());
assertThat(harness.getExitCode()).as(harness.getStderr()).isEqualTo(ExitCodes.SUCCESS);
assertThat(Files.readString(outputDir.resolve("README.md")))
.contains("custom-project", "com.example");
}
/***************************************************************************
* An unknown template must not fall back to an interactive selection.
*
* @since 0.2.1
***************************************************************************/
@Test
void testInit_noPrompt_unknownTemplateReturnsNotFound()
{
Path outputDir = tempDir.resolve("unknown-template");
harness.execute("qqq", "init", "missing-template", "--no-prompt",
"-o", outputDir.toString());
assertThat(harness.getExitCode()).isEqualTo(ExitCodes.NOT_FOUND);
assertThat(outputDir).doesNotExist();
}
/***************************************************************************
* Invalid required overrides must fail instead of prompting or using defaults.
*
* @since 0.2.1
***************************************************************************/
@Test
void testInit_noPrompt_rejectsEmptyRequiredOverride()
{
Path outputDir = tempDir.resolve("invalid-override");
harness.execute("qqq", "init", "test-with-prompts", "--no-prompt",
"--var", "projectName=", "-o", outputDir.toString());
assertThat(harness.getExitCode()).isEqualTo(ExitCodes.VALIDATION);
assertThat(outputDir).doesNotExist();
}
/***************************************************************************
* Test combined flags work together.
*
@@ -613,19 +613,20 @@ class InitCommandFullE2ETest
/***************************************************************************
* Test behavior when requesting a template that doesn't exist.
*
* Interactive selection can accept the first available fallback template.
* With TestTemplateRegistry, non-existent templates cause the system
* to fall back to the first available template in --no-prompt mode.
*
* @since 0.2.0
***************************************************************************/
@Test
void testInit_unknownTemplate_interactiveFallbackToFirst() throws IOException
void testInit_unknownTemplate_fallsBackToFirst() throws IOException
{
Path outputDir = tempDir.resolve("unknown-template-test");
//////////////////////////////////////////////////////////////////////////
// The empty input accepts the first interactive selection. //
// With --no-prompt, requesting unknown template uses first available //
//////////////////////////////////////////////////////////////////////////
harness.execute("qqq", "init", "non-existent-template",
harness.execute("qqq", "init", "non-existent-template", "--no-prompt",
"-o", outputDir.toString());
//////////////////////////////////////////////////////////////////////////
@@ -37,7 +37,6 @@ import io.qrun.qctl.qqq.template.ConsoleUI;
import io.qrun.qctl.qqq.template.MergeOptions;
import io.qrun.qctl.qqq.template.PostGenHookRunner;
import io.qrun.qctl.qqq.template.PromptRunner;
import io.qrun.qctl.qqq.template.PromptValidator;
import io.qrun.qctl.qqq.template.TemplateEngine;
import io.qrun.qctl.qqq.template.TemplateManifest;
import io.qrun.qctl.qqq.template.TemplateRenderException;
@@ -173,21 +172,6 @@ public class InitCommand implements Callable<Integer>
// Step 4: Collect variables (prompts + CLI overrides)
Map<String, String> allVars = new LinkedHashMap<>();
if(noPrompt && manifest.prompts() != null)
{
PromptValidator validator = new PromptValidator();
for(TemplateManifest.Prompt prompt : manifest.prompts())
{
String value = variables.getOrDefault(prompt.name(), prompt.defaultValue());
PromptValidator.ValidationResult result = validator.validate(prompt, value);
if(!result.valid())
{
ui.error(result.errorMessage());
return ExitCodes.VALIDATION;
}
allVars.put(prompt.name(), value);
}
}
if(!noPrompt && manifest.prompts() != null && !manifest.prompts().isEmpty())
{
PromptRunner promptRunner = new PromptRunner();
@@ -334,12 +318,6 @@ public class InitCommand implements Callable<Integer>
List<String> templateIds = templates.stream().map(TemplateInfo::id).toList();
SuggestionEngine suggestionEngine = new SuggestionEngine();
List<String> suggestions = suggestionEngine.findSimilar(templateName, templateIds);
if(noPrompt)
{
throw new TemplateError(TemplateError.ErrorCategory.TEMPLATE_NOT_FOUND,
"Template '" + templateName + "' not found.", suggestions,
"Use 'qctl qqq list' to list available templates");
}
ui.error("Template '" + templateName + "' not found.");
if(!suggestions.isEmpty())
@@ -409,9 +409,7 @@ public class TemplateEngine
// This allows templates to use __VARNAME__ in paths, which is safer //
// for shells and build tools that might interpret $ as a variable. //
/////////////////////////////////////////////////////////////////////////
// Windows separators otherwise escape the following Velocity variable.
String normalizedPath = path.replace('\\', '/')
.replaceAll("__([a-zA-Z][a-zA-Z0-9]*)__", "\\$$1");
String normalizedPath = path.replaceAll("__([a-zA-Z][a-zA-Z0-9]*)__", "\\$$1");
return renderContent(normalizedPath, variables);
}
@@ -15,7 +15,6 @@ package io.qrun.qctl.qqq.template;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
@@ -45,19 +44,11 @@ public class TemplateResolver
public Path resolve(String source, String version) throws IOException
{
// Local path
try
Path localPath = Path.of(source);
if(Files.isDirectory(localPath))
{
Path localPath = Path.of(source);
if(Files.isDirectory(localPath))
{
System.out.println("Using local template: " + localPath.toAbsolutePath());
return localPath.toAbsolutePath();
}
}
catch(InvalidPathException e)
{
// Remote URLs are not valid filesystem paths on Windows.
// Validate them below using the supported Git URL formats.
System.out.println("Using local template: " + localPath.toAbsolutePath());
return localPath.toAbsolutePath();
}
// Git URL or GitHub shorthand
@@ -303,24 +303,6 @@ class TemplateEngineTest
/***************************************************************************
* Windows separators must not escape path placeholders in Velocity.
*
* @throws TemplateRenderException if rendering fails
* @since 0.2.1
***************************************************************************/
@Test
void testRenderWindowsPathVariables() throws TemplateRenderException
{
Map<String, String> variables = Map.of("packagePath", "com/acme/orders");
assertThat(engine.renderPath("src\\main\\java\\$packagePath\\App.java", variables))
.isEqualTo("src/main/java/com/acme/orders/App.java");
assertThat(engine.renderPath("src\\main\\java\\__packagePath__\\App.java", variables))
.isEqualTo("src/main/java/com/acme/orders/App.java");
}
/***************************************************************************
* Test binary files are copied without modification.
***************************************************************************/