Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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](SSH-and-tokens)). `.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,20 @@
|
||||
# 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](Getting-started) | Requirements, install and the first run |
|
||||
| [SSH and tokens](SSH-and-tokens) | SSH access to Gitea and the two access tokens |
|
||||
| [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](SSH-and-tokens) |
|
||||
| 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,4 @@
|
||||
- [Home](Home)
|
||||
- [Getting started](Getting-started)
|
||||
- [SSH and tokens](SSH-and-tokens)
|
||||
- [Troubleshooting](Troubleshooting)
|
||||
Reference in New Issue
Block a user