Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3333eb7d4 | ||
|
|
44ee25d97d | ||
|
|
32fa6a819e | ||
|
|
a06b54ee7e | ||
|
|
7e9551e151 | ||
|
|
fa546d4134 | ||
|
|
01cc6a4548 | ||
|
|
cc03cf420d | ||
|
|
88eac33602 | ||
|
|
6a5da649da | ||
|
|
ca07b0669a | ||
|
|
43ce34267e | ||
|
|
e08d285d3b | ||
|
|
bcffd1af68 | ||
|
|
3e0047f36f | ||
|
|
56d44989ca | ||
|
|
642ea775d1 | ||
|
|
7c58c47d8e | ||
|
|
e23ef3a4b6 | ||
|
|
cc157816c7 | ||
|
|
e9872fa3ed | ||
|
|
4287d218ae |
@@ -7,3 +7,6 @@ end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.sh]
|
||||
switch_case_indent = true
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Quality
|
||||
|
||||
# Calls the shared quality-check workflow of TirSystem/github-action
|
||||
# (contract: ADR-0005 there). It runs shellcheck and shfmt on the tracked
|
||||
# shell files and changes no file. Configuration comes from .shellcheckrc
|
||||
# and .editorconfig in this repository. The release workflow requires this run
|
||||
# to be green on the commit it releases.
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
quality:
|
||||
permissions:
|
||||
contents: read
|
||||
# Pinned to an exact tag, never to main; tools-ref pins the script to the
|
||||
# same tag, so the whole check is fixed.
|
||||
uses: TirSystem/github-action/.gitea/workflows/quality-checks.yml@v1.1.0
|
||||
with:
|
||||
languages: shell
|
||||
tools-ref: v1.1.0
|
||||
@@ -0,0 +1,56 @@
|
||||
name: Release
|
||||
|
||||
# Releases this repository: creates the version tag and the release page from
|
||||
# the CHANGELOG.md entry, and only when the commit is the tip of main, its
|
||||
# Quality run is green and the changelog has an entry for the version. The
|
||||
# script is a file of TirSystem/github-action (tools/release.py), fetched at an
|
||||
# exact tag. dry_run defaults to true: it checks and prints and writes nothing,
|
||||
# so a release is a deliberate second run with dry_run false.
|
||||
#
|
||||
# release.py also keeps a moving major tag (v0 for 0.x versions) pointing to
|
||||
# the highest release of its major version, as in TirSystem/github-action.
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Version to release, MAJOR.MINOR.PATCH without a leading v"
|
||||
required: true
|
||||
dry_run:
|
||||
description: "true checks only; false creates the tag and the release page"
|
||||
required: false
|
||||
default: "true"
|
||||
|
||||
concurrency:
|
||||
group: release
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
release:
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Check out the repository
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Fetch the shared script
|
||||
uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
repository: TirSystem/github-action
|
||||
ref: v1.1.0
|
||||
path: shared-scripts
|
||||
|
||||
- name: Check and publish the release
|
||||
env:
|
||||
GITEA_API_URL: ${{ gitea.api_url }}
|
||||
SOURCE_REPOSITORY: ${{ gitea.repository }}
|
||||
RELEASE_CREDENTIALS: ${{ secrets.TOKEN_FOR_GITEA_RELEASE || github.token }}
|
||||
RELEASE_VERSION: ${{ inputs.version }}
|
||||
RELEASE_DRY_RUN: ${{ inputs.dry_run }}
|
||||
RELEASE_SHA: ${{ gitea.sha }}
|
||||
# The status context of the Quality run on the commit. Check the exact
|
||||
# text on a pull request and correct it here if the dry run says the
|
||||
# run was not found.
|
||||
VALIDATION_CONTEXT: "Quality / quality (push)"
|
||||
run: python3 shared-scripts/tools/release.py
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
[submodule "framework"]
|
||||
path = framework
|
||||
url = ssh://git@git.tirsystem.com:10022/TirSystem/SQA-QC-Framework.git
|
||||
url = ssh://git@git.tirsystem.com:10022/TirSystem/sqa-qc-framework.git
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to RepoFoundry. Versions are tags `vMAJOR.MINOR.PATCH`.
|
||||
The release workflow takes the text of a release page from the entry of its
|
||||
version, so every release needs an entry here before it is started.
|
||||
|
||||
Each release lists its changes in these groups, in this order: **Breaking**,
|
||||
**Added**, **Changed**, **Fixed**. A release with a Breaking entry has a new
|
||||
major version.
|
||||
|
||||
`python tools/changelog.py check` of `TirSystem/github-action` verifies this
|
||||
file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Gitea workflows: Quality (shellcheck and shfmt through the shared reusable
|
||||
workflow) and Release (tag and release page from this changelog).
|
||||
- A `wiki/` folder with a `.publish` marker, published to the Gitea wiki by the
|
||||
shared Publish wiki workflow.
|
||||
- `switch_case_indent` for `*.sh` in `.editorconfig`, so `shfmt` accepts the
|
||||
existing layout of `case` branches.
|
||||
@@ -8,7 +8,8 @@ RepoFoundry (`src/create-project.sh`) sets up a new project in one run:
|
||||
- and a **local project** with one credential-free remote, `origin` (Gitea), and the
|
||||
[SQA-QC-Framework](https://git.tirsystem.com/TirSystem/SQA-QC-Framework)
|
||||
added as a git submodule, with its skills, git hooks (and optionally the plan
|
||||
gate) and templates installed.
|
||||
gate) and templates installed. The files it installs (`.claude`, `.agents` and
|
||||
`AGENTS.md`) are excluded from git in the new project.
|
||||
|
||||
It is a Bash script. It asks for the repository name, description, visibility
|
||||
and owner (a user or an organization, separately on each host), shows a plan,
|
||||
@@ -51,8 +52,8 @@ Requirements:
|
||||
| `jq` (optional) | JSON parsing; without it a small built-in reader is used |
|
||||
|
||||
```bash
|
||||
git clone https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry.git
|
||||
cd RepoFoundry
|
||||
git clone https://git.tirsystem.com/TirSystem-BashScript/repo-foundry.git
|
||||
cd repo-foundry
|
||||
src/create-project.sh --help
|
||||
```
|
||||
|
||||
@@ -68,7 +69,7 @@ command (below):
|
||||
|
||||
```bash
|
||||
cd ~/work # the folder that will hold my-app
|
||||
~/src/RepoFoundry/src/create-project.sh # creates ~/work/my-app
|
||||
~/src/repo-foundry/src/create-project.sh # creates ~/work/my-app
|
||||
```
|
||||
|
||||
### Make it a global command
|
||||
@@ -103,7 +104,7 @@ run `export MSYS=winsymlinks:nativestrict` before the `ln -s`, or use an alias
|
||||
in `~/.bashrc`, which works because the script finds its own folder:
|
||||
|
||||
```bash
|
||||
alias repo-foundry='bash /c/Users/me/RepoFoundry/src/create-project.sh'
|
||||
alias repo-foundry='bash /c/Users/me/repo-foundry/src/create-project.sh'
|
||||
```
|
||||
|
||||
## Configuration
|
||||
@@ -259,6 +260,7 @@ Plan:
|
||||
Framework : add ssh://git@git.example.org:10022/Team/SQA-QC-Framework.git as a submodule
|
||||
Skills and hooks : install once; plan gate no
|
||||
Templates : AGENTS.md and docs/artifact-registry.md (you are asked before a file is replaced)
|
||||
Git excludes : /.claude /.agents /AGENTS.md go into .git/info/exclude (no tracked file changes)
|
||||
```
|
||||
|
||||
Without `--apply` that is all that happens. With `--apply` the script asks
|
||||
@@ -272,7 +274,10 @@ Without `--apply` that is all that happens. With `--apply` the script asks
|
||||
repository holds the license commit, that history;
|
||||
5. the framework as the submodule `framework`;
|
||||
6. the framework's skills and git hooks, and the plan gate if chosen;
|
||||
7. `AGENTS.md` and `docs/artifact-registry.md` from the framework's templates.
|
||||
7. `AGENTS.md` and `docs/artifact-registry.md` from the framework's templates;
|
||||
8. `/.claude`, `/.agents` and `/AGENTS.md` added to the new project's
|
||||
`.git/info/exclude`, so git does not list them (see
|
||||
[The files git ignores](#the-files-git-ignores-in-the-new-project)).
|
||||
|
||||
No commit is made in the new project. Work on a branch there: the framework's
|
||||
hooks refuse commits on `main`.
|
||||
@@ -298,6 +303,39 @@ it replaces an existing `core.hooksPath`, and before it replaces an existing
|
||||
replaces a remote that points somewhere else, and git itself refuses to
|
||||
overwrite a file when the license history is checked out.
|
||||
|
||||
### The files git ignores in the new project
|
||||
|
||||
The framework installs files that are copies, not the project's own work. After
|
||||
the templates are copied, the script makes git ignore three paths in the new
|
||||
project by adding them to its `.git/info/exclude`:
|
||||
|
||||
| Path | What it is |
|
||||
| --- | --- |
|
||||
| `/.claude` | the skills for the standalone Claude Code CLI (the whole folder) |
|
||||
| `/.agents` | the skills for Codex CLI and other tools that read `.agents/skills` (the whole folder) |
|
||||
| `/AGENTS.md` | the project instructions, copied from the framework's template |
|
||||
|
||||
- **Nothing is committed and no tracked file changes.** `.gitignore` is not
|
||||
touched, and `framework`, `.gitmodules` and `docs/artifact-registry.md` stay
|
||||
visible to git: they are part of the project.
|
||||
- **The entries belong to this clone.** `.git/info/exclude` is never committed
|
||||
or pushed, so a fresh clone has neither the entries nor these files. There,
|
||||
run `git submodule update --init --recursive` and then
|
||||
`bash framework/scripts/install-skills.sh` to make the skills again.
|
||||
`AGENTS.md` cannot be made again that way: it is a copy of a template that
|
||||
you edit, so what you add to it exists only in the clone where you wrote it.
|
||||
- **The whole `.claude` and `.agents` folders are ignored,** not only their
|
||||
`skills` folders, so anything else you keep there (settings, agents) is
|
||||
ignored too.
|
||||
- **A path git already tracks stays tracked,** because an exclusion does not
|
||||
apply to a tracked file. The script never removes anything from git; the
|
||||
summary names the path ("git tracks AGENTS.md, so it is not ignored"). Run
|
||||
`git rm --cached` on it yourself if you want it ignored.
|
||||
- **Without SSH to Gitea** the framework steps are skipped, and so is this one.
|
||||
- **To track one of the paths anyway,** delete its line from
|
||||
`.git/info/exclude` in the project and `git add` it. Running the script again
|
||||
in that directory adds the line back.
|
||||
|
||||
## SSH access to Gitea
|
||||
|
||||
The framework submodule is fetched over SSH on port **10022**
|
||||
@@ -410,6 +448,7 @@ step, `2` a usage error.
|
||||
| A step fails after another succeeded | stops and prints what exists, what failed and how to continue | fix the cause and run the **same command again with `--apply`**: what was created is offered for reuse |
|
||||
| The mirror is refused (disabled, interval too short) | keeps the repositories and reports it | change `MIRROR_INTERVAL` or ask the Gitea administrator, then run again |
|
||||
| The framework submodule cannot be fetched | reports the address and how to test SSH | fix your SSH access, run again |
|
||||
| Git already tracks `.claude`, `.agents` or `AGENTS.md` in the project | leaves it tracked and names it in the summary; the other paths are excluded | `git rm --cached` it if you want it ignored |
|
||||
| `sync_on_commit` was ignored by Gitea | warns; the mirror syncs on its interval | enable it in the repository settings if needed |
|
||||
|
||||
A partial run is reported like this:
|
||||
@@ -453,6 +492,10 @@ web interface and the project directory by hand.
|
||||
copy.
|
||||
- **The framework needs SSH.** Without SSH access to Gitea the framework steps
|
||||
can only be skipped.
|
||||
- **The ignored framework files are not shared.** `.claude`, `.agents` and
|
||||
`AGENTS.md` are excluded in the clone the script made, not in the repository,
|
||||
so another clone does not have them (see
|
||||
[The files git ignores](#the-files-git-ignores-in-the-new-project)).
|
||||
- **Tested on Windows (Git Bash) only so far.** Running the tests on Linux and
|
||||
macOS is an open follow-up.
|
||||
|
||||
@@ -484,9 +527,9 @@ file. The files are loaded from that directory only, by a fixed path.
|
||||
| `plan.sh` | printing what the script is about to do |
|
||||
| `repositories.sh` | creating the GitHub and Gitea repositories |
|
||||
| `mirror.sh` | the Gitea to GitHub push mirror |
|
||||
| `git.sh` | running git for the new project without prompts or tokens on a command line |
|
||||
| `git.sh` | running git for the new project without prompts or tokens on a command line, and adding entries to its `.git/info/exclude` |
|
||||
| `localproject.sh` | the local directory, git repository and remotes |
|
||||
| `framework.sh` | the framework submodule, skills, hooks and templates |
|
||||
| `framework.sh` | the framework submodule, skills, hooks and templates, and the git excludes for the files it installs |
|
||||
| `apply.sh` | confirmations and the apply flow; the only code that changes anything |
|
||||
| `cli.sh` | usage text and option parsing |
|
||||
|
||||
|
||||
+1
-1
Submodule framework updated: 14d221ec1c...d2757c98df
@@ -8,7 +8,7 @@
|
||||
# shellcheck disable=SC2034 # read and written by the other library files
|
||||
|
||||
readonly PROJECT_NAME="${REPOFOUNDRY_NAME:-RepoFoundry}"
|
||||
readonly VERSION="0.3.1"
|
||||
readonly VERSION="0.3.3"
|
||||
readonly EXIT_FAILURE=1
|
||||
readonly EXIT_USAGE=2
|
||||
readonly MAX_VALUE_LENGTH=2048
|
||||
|
||||
@@ -291,6 +291,7 @@ echo done'
|
||||
local exclude
|
||||
exclude="$(cat "$WORK/p/.git/info/exclude")"
|
||||
assert_contains "old entry kept" "$exclude" "build/"
|
||||
assert_contains "comment line" "$exclude" "# RepoFoundry: the credentials file of this project"
|
||||
assert_eq "entry added once" "1" "$(grep -c '^\.env$' "$WORK/p/.git/info/exclude")"
|
||||
assert_eq "old entry still on its own line" "1" "$(grep -c '^build/$' "$WORK/p/.git/info/exclude")"
|
||||
}
|
||||
|
||||
@@ -282,6 +282,105 @@ test_existing_template_targets_are_replaced_only_after_a_yes() {
|
||||
assert_contains "reported" "$OUT" "kept existing AGENTS.md; copied docs/artifact-registry.md"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------- git excludes
|
||||
|
||||
# exclude_count FILE LINE: how many lines of FILE are exactly LINE.
|
||||
exclude_count() {
|
||||
grep -cxF -e "$2" "$1" || true
|
||||
}
|
||||
|
||||
test_the_framework_files_are_ignored_by_git_and_nothing_else_changes() {
|
||||
setup_hosts
|
||||
local dir="$WORK/project" exclude path listing
|
||||
exclude="$dir/.git/info/exclude"
|
||||
local_answers "$dir" y n
|
||||
# create now, and create the project's .env as well
|
||||
run_apply "$LOCAL_ANSWERS"$'y\ny\n'
|
||||
assert_status "apply" 0 "$STATUS"
|
||||
assert_contains "reported" "$OUT" "Git excludes : created (/.claude /.agents /AGENTS.md added to .git/info/exclude)"
|
||||
for path in /.claude /.agents /AGENTS.md; do
|
||||
assert_eq "one entry for $path" "1" "$(exclude_count "$exclude" "$path")"
|
||||
done
|
||||
for path in .claude .agents AGENTS.md .claude/skills/coding-conventions/SKILL.md .agents/skills/.framework-skills; do
|
||||
check
|
||||
if ! project_git "$dir" check-ignore -q -- "$path"; then
|
||||
fail "$path is not ignored by git"
|
||||
fi
|
||||
done
|
||||
for path in framework .gitmodules docs/artifact-registry.md; do
|
||||
check
|
||||
if project_git "$dir" check-ignore -q --no-index -- "$path"; then
|
||||
fail "$path is ignored by git but is part of the project"
|
||||
fi
|
||||
done
|
||||
listing="$(project_git "$dir" status --porcelain)"
|
||||
assert_not_contains ".claude is not listed" "$listing" ".claude"
|
||||
assert_not_contains ".agents is not listed" "$listing" ".agents"
|
||||
assert_not_contains "AGENTS.md is not listed" "$listing" "AGENTS.md"
|
||||
assert_contains "the submodule is listed" "$listing" "framework"
|
||||
assert_contains "the registry is listed" "$listing" "docs/"
|
||||
assert_file_missing "no .gitignore is written" "$dir/.gitignore"
|
||||
assert_eq "nothing was committed" "1" "$(project_git "$dir" rev-list --count HEAD)"
|
||||
assert_eq "the .env keeps its own entry, once" "1" "$(exclude_count "$exclude" ".env")"
|
||||
assert_contains "the .env comment is unchanged" "$(cat "$exclude")" "# RepoFoundry: the credentials file of this project"
|
||||
}
|
||||
|
||||
test_the_git_excludes_are_written_once_and_keep_the_existing_lines() {
|
||||
local dir="$WORK/p" exclude="$WORK/p/.git/info/exclude" path
|
||||
mkdir -p "$dir"
|
||||
project_git "$dir" init -q
|
||||
printf 'build/' >"$exclude" # no trailing newline
|
||||
run_lib "" 'PROJECT[directory]="'"$dir"'"
|
||||
exclude_framework_files
|
||||
echo "${STEP_STATUS["Git excludes"]}"
|
||||
exclude_framework_files
|
||||
echo "${STEP_STATUS["Git excludes"]} ${STEP_DETAIL["Git excludes"]}"'
|
||||
assert_status "run" 0 "$STATUS"
|
||||
assert_eq "created, then reused" $'created\nreused (/.claude /.agents /AGENTS.md already excluded)' "$OUT"
|
||||
assert_eq "the old line is kept on its own line" "1" "$(exclude_count "$exclude" "build/")"
|
||||
for path in /.claude /.agents /AGENTS.md; do
|
||||
assert_eq "entry for $path written once" "1" "$(exclude_count "$exclude" "$path")"
|
||||
done
|
||||
assert_eq "one comment line" "1" "$(exclude_count "$exclude" "# RepoFoundry: the files installed from the framework")"
|
||||
}
|
||||
|
||||
test_nothing_is_written_for_paths_git_already_ignores() {
|
||||
local dir="$WORK/p" exclude="$WORK/p/.git/info/exclude"
|
||||
mkdir -p "$dir"
|
||||
project_git "$dir" init -q
|
||||
printf '.claude\n.agents\nAGENTS.md\n' >"$dir/.gitignore"
|
||||
run_lib "" 'PROJECT[directory]="'"$dir"'"
|
||||
exclude_framework_files
|
||||
echo "${STEP_STATUS["Git excludes"]}"'
|
||||
assert_status "run" 0 "$STATUS"
|
||||
assert_eq "reused" "reused" "$OUT"
|
||||
assert_eq "no entry written" "0" "$(grep -c '^/' "$exclude" || true)"
|
||||
assert_eq "no comment written" "0" "$(exclude_count "$exclude" "# RepoFoundry: the files installed from the framework")"
|
||||
}
|
||||
|
||||
test_a_tracked_framework_file_stays_tracked_and_is_named() {
|
||||
local dir="$WORK/p"
|
||||
mkdir -p "$dir/.agents" "$dir/.claude"
|
||||
project_git "$dir" init -q
|
||||
printf 'mine\n' >"$dir/AGENTS.md"
|
||||
printf 'x\n' >"$dir/.agents/keep"
|
||||
printf 'y\n' >"$dir/.claude/new" # not tracked
|
||||
project_git "$dir" add AGENTS.md .agents/keep
|
||||
project_git "$dir" commit -q -m seed
|
||||
run_lib "" 'PROJECT[directory]="'"$dir"'"
|
||||
exclude_framework_files
|
||||
echo "${STEP_STATUS["Git excludes"]} ${STEP_DETAIL["Git excludes"]}"'
|
||||
assert_status "run" 0 "$STATUS"
|
||||
assert_contains "names the tracked paths" "$OUT" "git tracks .agents AGENTS.md, so it is not ignored"
|
||||
assert_eq "both are still tracked" $'.agents/keep\nAGENTS.md' "$(project_git "$dir" ls-files)"
|
||||
assert_eq "the content is unchanged" "mine" "$(cat "$dir/AGENTS.md")"
|
||||
assert_eq "nothing is staged or modified" "" "$(project_git "$dir" status --porcelain)"
|
||||
check
|
||||
if ! project_git "$dir" check-ignore -q -- .claude; then
|
||||
fail ".claude, which git does not track, is not ignored"
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------- SSH and errors
|
||||
|
||||
test_without_ssh_the_run_stops_unless_the_framework_is_skipped() {
|
||||
@@ -311,6 +410,9 @@ test_without_ssh_the_framework_steps_are_skipped_after_a_yes() {
|
||||
assert_contains "framework skipped" "$OUT" "Framework : skipped (no SSH access to Gitea)"
|
||||
assert_contains "skills and hooks skipped" "$OUT" "Skills and hooks : skipped (no SSH access to Gitea)"
|
||||
assert_contains "templates skipped" "$OUT" "Templates : skipped (no SSH access to Gitea)"
|
||||
assert_contains "git excludes skipped" "$OUT" "Git excludes : skipped (no SSH access to Gitea)"
|
||||
assert_not_contains "nothing excluded: .claude" "$(cat "$dir/.git/info/exclude")" "/.claude"
|
||||
assert_not_contains "nothing excluded: AGENTS.md" "$(cat "$dir/.git/info/exclude")" "/AGENTS.md"
|
||||
assert_file_missing "no submodule" "$dir/.gitmodules"
|
||||
assert_file_missing "no AGENTS.md" "$dir/AGENTS.md"
|
||||
}
|
||||
@@ -365,6 +467,7 @@ test_the_dry_run_plan_describes_the_local_steps_and_creates_nothing() {
|
||||
assert_contains "framework" "$OUT" "Framework : add $SSH_FRAMEWORK_URL as a submodule"
|
||||
assert_contains "skills and hooks" "$OUT" "Skills and hooks : install once; plan gate yes"
|
||||
assert_contains "templates" "$OUT" "Templates : AGENTS.md and docs/artifact-registry.md (you are asked before a file is replaced)"
|
||||
assert_contains "git excludes" "$OUT" "Git excludes : /.claude /.agents /AGENTS.md go into .git/info/exclude (no tracked file changes)"
|
||||
assert_file_missing "nothing created" "$dir"
|
||||
}
|
||||
|
||||
@@ -378,6 +481,7 @@ test_the_plan_marks_an_existing_directory_and_missing_ssh() {
|
||||
run_dry "$LOCAL_ANSWERS"
|
||||
assert_contains "existing directory" "$OUT" "use the existing directory $dir, which has files (you will be asked)"
|
||||
assert_contains "no SSH" "$OUT" "NOT possible without SSH to Gitea; you will be asked whether to go on without it"
|
||||
assert_not_contains "no git excludes without the framework" "$OUT" "Git excludes"
|
||||
assert_contains "HTTPS origin" "$OUT" "will use HTTPS (SSH test: failed"
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ test_usage_errors() {
|
||||
assert_contains "help shows exit codes" "$OUT" "Exit codes"
|
||||
run_cli "" --version
|
||||
assert_status "version" 0 "$STATUS"
|
||||
assert_contains "version output" "$OUT" "RepoFoundry 0.3.1"
|
||||
assert_contains "version output" "$OUT" "RepoFoundry 0.3.3"
|
||||
}
|
||||
|
||||
test_warns_when_env_is_not_ignored_by_git() {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# Getting started
|
||||
|
||||
## Requirements
|
||||
|
||||
| Tool | Needed for |
|
||||
| --- | --- |
|
||||
| bash 4.4 or later | the script (macOS ships 3.2: install a newer bash first) |
|
||||
| `git` | the local project and the framework submodule |
|
||||
| `curl` | the GitHub and Gitea APIs |
|
||||
| `ssh` (optional) | the SSH check; without it the framework steps are skipped |
|
||||
| `jq` (optional) | JSON parsing; without it a small built-in reader is used |
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
git clone https://git.tirsystem.com/TirSystem-BashScript/repo-foundry.git
|
||||
cd repo-foundry
|
||||
src/create-project.sh --help
|
||||
```
|
||||
|
||||
Nothing is installed system-wide: the script runs from the checkout.
|
||||
|
||||
## Configure
|
||||
|
||||
Copy `config.env.example` to `config.env` and `.env.example` to `.env`, then set the service addresses and the tokens (see [SSH and tokens](SshAndTokens)). `.env` holds credentials and is never committed.
|
||||
|
||||
## Run
|
||||
|
||||
Start the script from the folder that should hold the new project:
|
||||
|
||||
```bash
|
||||
src/create-project.sh # dry run: reads from the hosts, creates nothing
|
||||
src/create-project.sh --apply # creates everything after a final yes
|
||||
```
|
||||
|
||||
No commit is made in the new project. Work on a branch there: the framework's hooks refuse commits on `main`.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Repo Foundry
|
||||
|
||||
Repo Foundry (`src/create-project.sh`) sets up a new project in one run:
|
||||
|
||||
- a **Gitea** repository, the source of truth;
|
||||
- optionally an empty **GitHub** repository that receives everything through a push mirror from Gitea;
|
||||
- a **local project** with one credential-free remote, `origin`, and the SQA-QC-Framework as a git submodule, with its skills, git hooks and templates installed.
|
||||
|
||||
It is a Bash script. It shows a plan first and creates nothing until you pass `--apply` and answer yes.
|
||||
|
||||
## Pages
|
||||
|
||||
| Page | What it covers |
|
||||
| --- | --- |
|
||||
| [Getting started](GettingStarted) | Requirements, install and the first run |
|
||||
| [SSH and tokens](SshAndTokens) | SSH access to Gitea and the two access tokens |
|
||||
| [Updating the framework](UpdatingTheFramework) | Keep the framework submodule of a project up to date |
|
||||
| [Troubleshooting](Troubleshooting) | Common failures and what to do |
|
||||
|
||||
The full reference (configuration files, security decisions, error handling, known limitations) is the [README](https://git.tirsystem.com/TirSystem-BashScript/repo-foundry/src/branch/main/README.md).
|
||||
Report a problem as an [issue](https://git.tirsystem.com/TirSystem-BashScript/repo-foundry/issues).
|
||||
@@ -0,0 +1,25 @@
|
||||
# SSH and tokens
|
||||
|
||||
## SSH access to Gitea
|
||||
|
||||
The framework submodule is fetched over SSH on port **10022**. Before you run the script:
|
||||
|
||||
1. Add your SSH public key to your Gitea account.
|
||||
2. Connect once by hand so the server's host key is known (the script refuses unknown host keys):
|
||||
|
||||
```bash
|
||||
ssh -p 10022 -T git@git.tirsystem.com
|
||||
```
|
||||
|
||||
A message that you have successfully authenticated, without shell access, means it works.
|
||||
|
||||
> **Passphrase keys.** The script never prompts for a passphrase. A key with a passphrase must already be loaded in `ssh-agent` (`ssh-add`). This is tracked in [issue 77](https://git.tirsystem.com/TirSystem-BashScript/repo-foundry/issues/77).
|
||||
|
||||
## Tokens
|
||||
|
||||
| Token | Key in `.env` | Needed | Type |
|
||||
| --- | --- | --- | --- |
|
||||
| Gitea access token | `GITEA_TOKEN` | always | access token with scopes |
|
||||
| GitHub personal access token | `GITHUB_PAT` | only when you choose GitHub | **classic** personal access token |
|
||||
|
||||
Step by step with pictures: [how to create the access tokens](https://git.tirsystem.com/TirSystem-BashScript/repo-foundry/src/branch/main/howto/create-access-tokens.md). The exact permissions are under [Token permissions](https://git.tirsystem.com/TirSystem-BashScript/repo-foundry/src/branch/main/README.md#token-permissions) in the README.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Troubleshooting
|
||||
|
||||
| Symptom | What to check |
|
||||
| --- | --- |
|
||||
| The plan says the SSH test failed | Your key is in your Gitea account, the host key is known (`ssh -p 10022 -T git@git.tirsystem.com`), and a passphrase key is loaded with `ssh-add` |
|
||||
| The script stops at the tool check | bash is 4.4 or later, and `git` and `curl` are installed |
|
||||
| A host rejects the token | The token is current and has the scopes in [SSH and tokens](SshAndTokens) |
|
||||
| A run stopped half way | Fix the cause and run the same command again with `--apply`: what was already created is offered for reuse |
|
||||
|
||||
More: [Error handling and recovery](https://git.tirsystem.com/TirSystem-BashScript/repo-foundry/src/branch/main/README.md#error-handling-and-recovery) and [Known limitations](https://git.tirsystem.com/TirSystem-BashScript/repo-foundry/src/branch/main/README.md#known-limitations) in the README.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Updating the framework
|
||||
|
||||
A project created by Repo Foundry holds the SQA-QC-Framework as the git submodule `framework`. A clone or a `git pull` does not move it: the project stays on the framework commit it recorded. To keep the framework up to date, run this in the project, from its root:
|
||||
|
||||
```bash
|
||||
git submodule update --init --remote --recursive framework
|
||||
```
|
||||
|
||||
| Part | Why |
|
||||
| --- | --- |
|
||||
| `--init` | fetches the submodule if it is not there yet |
|
||||
| `--remote` | moves `framework` to the latest commit of the framework repository, instead of the commit the project recorded |
|
||||
| `--recursive` | also updates the submodule the framework holds itself (`framework/qc`, the checklists) |
|
||||
|
||||
## After the update
|
||||
|
||||
1. Re-install the skills, so the copies in `.claude/skills/` and `.agents/skills/` match the new framework:
|
||||
|
||||
```bash
|
||||
bash framework/scripts/install-skills.sh
|
||||
```
|
||||
|
||||
2. Review what changed, then commit the new framework commit on a branch (the framework's hooks refuse commits on `main`):
|
||||
|
||||
```bash
|
||||
git status
|
||||
git switch -c update-framework
|
||||
git add framework
|
||||
git commit -m "Update the framework"
|
||||
```
|
||||
|
||||
3. Bring `AGENTS.md` up to date (next section).
|
||||
|
||||
## Update AGENTS.md to the newest template
|
||||
|
||||
`AGENTS.md` in your project was copied from `framework/templates/AGENTS-template.md` when the project was created. It is a copy, so it does not change when the framework does. The template holds the rules the framework's skills and hooks expect (for example the supervised and autonomous modes), so bring it up to date after each framework update.
|
||||
|
||||
1. See what differs:
|
||||
|
||||
```bash
|
||||
diff -u AGENTS.md framework/templates/AGENTS-template.md
|
||||
```
|
||||
|
||||
2. If you never edited `AGENTS.md`, replace it:
|
||||
|
||||
```bash
|
||||
cp framework/templates/AGENTS-template.md AGENTS.md
|
||||
```
|
||||
|
||||
3. If you added your own rules, copy the new or changed template sections into `AGENTS.md` by hand and keep yours. Replacing the file would lose them.
|
||||
|
||||
Repo Foundry adds `/AGENTS.md` to `.git/info/exclude` in the new project, so git does not list the file and the change is not a commit: it only affects your working copy. Do not do the same with `docs/artifact-registry.md`: it holds your project's own rows, so only compare it with `framework/templates/artifact-registry-template.md` and copy what you need.
|
||||
|
||||
## Notes
|
||||
|
||||
- `--remote` follows the default branch of the framework repository, not a release tag. To stay on a release, check the tag out inside the submodule instead: `git -C framework checkout <tag>`, then commit `framework` as above.
|
||||
- Another person who clones the project, or pulls your commit, gets the recorded framework commit with `git submodule update --init --recursive`.
|
||||
- The submodule is fetched over SSH on port 10022, so the [SSH setup](SshAndTokens) applies to updates too.
|
||||
@@ -0,0 +1,5 @@
|
||||
- [Home](Home)
|
||||
- [Getting started](GettingStarted)
|
||||
- [SSH and tokens](SshAndTokens)
|
||||
- [Updating the framework](UpdatingTheFramework)
|
||||
- [Troubleshooting](Troubleshooting)
|
||||
Reference in New Issue
Block a user