MIL-004: project details preset in config.env (with the e2e fixes and the plan) #34
@@ -15,8 +15,12 @@ and owner (a user or an organization, separately on each host), shows a plan,
|
||||
and only creates anything after you pass `--apply` and answer yes.
|
||||
|
||||
> **Status.** The script is tested with stubbed host APIs and real git against
|
||||
> local repositories (see [Development](#development)). A first run against
|
||||
> real GitHub and Gitea repositories is still to be recorded.
|
||||
> local repositories (see [Development](#development)). A first end-to-end run
|
||||
> on real GitHub and Gitea repositories, with organization owners on both, has
|
||||
> passed (2026-10-05, review record RC-017). Creating a repository under your
|
||||
> own Gitea account needs the `write:user` token scope (see
|
||||
> [Token permissions](#token-permissions)); that path has not been completed
|
||||
> yet.
|
||||
|
||||
## Contents
|
||||
|
||||
@@ -63,7 +67,7 @@ are accepted, and anything else stops the run with a message that names the key
|
||||
and the line, never the value.
|
||||
|
||||
```bash
|
||||
cp config.env.example config.env # service addresses, not secret
|
||||
cp config.env.example config.env # service addresses, not secret: set GITEA_URL (and GITEA_API_URL)
|
||||
cp .env.example .env # credentials: keep private
|
||||
chmod 600 .env # Linux and macOS
|
||||
```
|
||||
@@ -195,7 +199,7 @@ repositories for the chosen owner and to push to the new one.
|
||||
| Create a private repository | classic token with the `repo` scope | GitHub REST documentation, "Create a repository" |
|
||||
| Create a public repository only | classic token with `public_repo` is enough | same |
|
||||
| Push from the Gitea mirror | covered by `repo` | |
|
||||
| Check that you belong to the organization owner | probably `read:org` | **Not confirmed**: the GitHub documentation names no scope for this call. If the script says you do not belong to an organization that you do belong to, add `read:org`. |
|
||||
| Check that you belong to the organization owner | worked with a classic token that has `repo` and `admin:org` | `read:org` alone was **not tested**: the GitHub documentation names no scope for this call. If the script says you do not belong to an organization that you do belong to, add `read:org`. |
|
||||
|
||||
- **Organization owners:** you must be an active member who is allowed to
|
||||
create repositories in the organization. Organizations that require SSO or
|
||||
@@ -211,9 +215,9 @@ repositories for the chosen owner and to push to the new one.
|
||||
| Need | Scope | Source |
|
||||
| --- | --- | --- |
|
||||
| Read the account the token belongs to | `read:user` | Gitea documentation |
|
||||
| Create repositories, manage the push mirror | `write:repository` | Gitea documentation |
|
||||
| Look up an organization and your permissions in it | `read:organization` | Gitea documentation |
|
||||
| Create a repository in an organization | probably `write:organization` as well | **Not confirmed**: expected from how the Gitea API groups organization calls; to be confirmed in the first end-to-end run. |
|
||||
| Create a repository **under your own account** | `write:user` | **Confirmed by a real server**: without it Gitea answers `required=[write:user]` |
|
||||
| Create a repository in an organization, manage its push mirror | `write:organization` and `write:repository` | worked with a token that has both, plus `read:user`; the minimum was not narrowed down |
|
||||
| Look up an organization and your permissions in it | covered by the scopes above | worked in the end-to-end run |
|
||||
|
||||
A missing scope shows up as an HTTP 403 with the server's own message. The
|
||||
script stops before it creates anything when a preflight check is refused.
|
||||
@@ -256,7 +260,7 @@ step, `2` a usage error.
|
||||
| A tool, a config key or a token is missing or invalid | stops before any request | fix it and run again |
|
||||
| A token is refused, an owner is unknown, a name is taken, the license is missing | stops in the preflight; nothing was created | fix the cause |
|
||||
| The host cannot be reached | stops with the host name | try again |
|
||||
| A repository already exists and is empty (Gitea: or holds only the license) | offers to reuse it (default no) | answer, or choose another name |
|
||||
| A repository already exists and is empty (Gitea: or holds only the license and the README Gitea adds) | offers to reuse it (default no) | answer, or choose another name |
|
||||
| A repository already has content | stops | choose another name or remove it |
|
||||
| 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 |
|
||||
@@ -285,17 +289,20 @@ web interface and the project directory by hand.
|
||||
this, and revoke it if the Gitea server is ever in doubt.
|
||||
- **`sync_on_commit` may be ignored.** When a push mirror is created through
|
||||
the API, some Gitea versions ignore `sync_on_commit` (upstream issue
|
||||
go-gitea/gitea#22990). The script reads the mirror back and warns if the
|
||||
setting was not applied; the mirror then syncs on its interval
|
||||
go-gitea/gitea#22990). On Gitea 1.27.3 it was applied: a branch pushed to
|
||||
Gitea reached GitHub within seconds. The script reads the mirror back and
|
||||
warns if the setting was not applied; the mirror then syncs on its interval
|
||||
(`MIRROR_INTERVAL`, default 10 minutes). The first sync is requested right
|
||||
after the mirror is created.
|
||||
- **The server decides the shortest interval** and whether push mirrors are
|
||||
allowed at all. A refused mirror stops the run with the server's message;
|
||||
the repositories created so far are kept.
|
||||
- **The license commit.** Gitea adds the license file when the repository is
|
||||
created with `auto_init`. The script sends no README, so the repository
|
||||
should hold only `LICENSE`; this is still to be confirmed against a real
|
||||
server.
|
||||
created with `auto_init`, and on the real server it also adds a generated
|
||||
`README.md`. Both are mirrored to GitHub and become the first commit of the
|
||||
local project. A Gitea repository that holds only these files counts as
|
||||
content this script created and is offered for reuse; a repository with
|
||||
anything else counts as having content and is refused.
|
||||
- **No rollback.** See [Error handling and recovery](#error-handling-and-recovery).
|
||||
- **Mirror direction is Gitea to GitHub only.** Push to Gitea; GitHub is a
|
||||
copy.
|
||||
|
||||
+2
-2
@@ -17,10 +17,10 @@ GITHUB_API_URL=https://api.github.com
|
||||
GITHUB_WEB_URL=https://github.com
|
||||
|
||||
# Gitea instance base URL. Repository links are derived from it.
|
||||
GITEA_URL=https://git.tirsystem.com/
|
||||
GITEA_URL=https://<your gitea instance>/
|
||||
|
||||
# Gitea REST API base URL. If you leave this out it is GITEA_URL + /api/v1.
|
||||
GITEA_API_URL=https://git.tirsystem.com/api/v1
|
||||
GITEA_API_URL=https://<your gitea instance>/api/v1
|
||||
|
||||
# Optional. SSH port of the Gitea server, used for the SSH check and later
|
||||
# for the framework submodule. Default: 10022.
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
## Version History
|
||||
| Date | Status | Author | Reviewer | Change | Commit |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-10-05 | Deprecated | Jens Tirsvad Nielsen | S02 | Initial version | [424f14f] |
|
||||
| 2026-10-05 | Accepted | Jens Tirsvad Nielsen | S02 | Optional GitHub; choosing GitHub applies the AGPL license to the Gitea repository<br>Cited US-001.02<br>Purpose and criterion 1 reworded for optional GitHub<br>Target date accepted | [02875ae] |
|
||||
| 2026-10-05 | Proposed | Jens Tirsvad Nielsen | S02 | Criterion 2 corrected after the live run: Gitea also adds a README.md with the license | pending |
|
||||
|
||||
---
|
||||
|
||||
@@ -27,7 +27,7 @@ Decide whether the script creates the Gitea repository and, if GitHub is chosen,
|
||||
| # | Criterion (objectively checkable) | Go | No-Go |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | Repositories are created under the owner chosen at the prompt, for a user owner and for an organization owner, on each host used | Both verified | Any under the wrong owner |
|
||||
| 2 | The GitHub repository is created empty. The Gitea repository holds only the AGPL license file when GitHub is chosen, otherwise it is empty. Neither has a generated README or `.gitignore` | Verified | Any other commit present |
|
||||
| 2 | The GitHub repository is created empty. The Gitea repository holds only the AGPL license file and the `README.md` that Gitea generates for it when GitHub is chosen, otherwise it is empty. Neither has a `.gitignore` | Verified | Any other file present |
|
||||
| 3 | When GitHub is chosen, a commit pushed to Gitea (including the license file) appears on GitHub; nothing flows the other way. When GitHub is not chosen, no GitHub call is made | Verified | Wrong direction, no sync, or a GitHub call without the choice |
|
||||
| 4 | Mirror credentials are never part of a remote URL, log or output | None found | Any found |
|
||||
| 5 | With an invalid token on one host, the script stops before creating anything or reports exactly what was created and how to continue | Verified | Silent or misleading |
|
||||
@@ -76,5 +76,4 @@ Decide whether the script creates the Gitea repository and, if GitHub is chosen,
|
||||
[US-001]: ../user-stories.md
|
||||
[UC-001]: ../uc-001/uc.md
|
||||
[MIL-001]: ./mil-001-foundation.md
|
||||
[424f14f]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/424f14f4f5577bb47fea41c8f3a655dca953e6d8
|
||||
[02875ae]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/02875aee5f2953473924074eea0056eb31af6b7a
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# SQA Review Record: End-to-end test and final security review (MIL-003)
|
||||
|
||||
## Metadata
|
||||
| Key | Value |
|
||||
| --- | --- |
|
||||
| ID | RC-017 |
|
||||
| CrossReference | [MIL-003], [MIL-002], [RC-016] |
|
||||
|
||||
## Version History
|
||||
| Date | Status | Author | Reviewer | Change | Commit |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-10-05 | Proposed | Jens Tirsvad Nielsen | S02 | Initial version | pending |
|
||||
|
||||
---
|
||||
|
||||
## Artifact Under Review
|
||||
|
||||
- Instance reviewed: the whole flow of `src/create-project.sh` (branch `mil-003-scaffold-and-release` plus the fixes below), run against real GitHub and Gitea repositories; this is task 6 (issue #20) of [MIL-003] and the live evidence for [MIL-002].
|
||||
- Checklist used: the Go/No-Go criteria of [MIL-003] and the credential, ownership, mirror, submodule and API items named in its task 6. No QC checklist covers an end-to-end run; the code itself was reviewed in [RC-016].
|
||||
- Review date: 2026-10-05
|
||||
- Hosts: Gitea 1.27.3 at `git.tirsystem.com` (SSH on port 10022) and GitHub; real tokens from `.env` (a classic GitHub token with `repo` and `admin:org`; a Gitea token with `write:repository`, `write:organization`, `read:user` and other scopes, but not `write:user`).
|
||||
|
||||
## End-to-end runs
|
||||
|
||||
| Run | Owners | Result |
|
||||
| --- | --- | --- |
|
||||
| Dry run | `Tirsvad` on both hosts | All preflight checks passed; nothing created. |
|
||||
| First `--apply` | `Tirsvad` on both hosts | Stopped before creating anything: **a defect** (see finding F1). |
|
||||
| A, after the fix | user `Tirsvad` on both hosts | GitHub repository created. Gitea refused: `required=[write:user]`, which the token lacks. The script stopped, reported what existed (GitHub created, Gitea FAILED, the rest not attempted) and how to continue, and deleted nothing. |
|
||||
| B | organization `TirSystem-BashScript` on both hosts, plan gate on | Everything created: both repositories, the mirror, the local project, the framework, skills, hooks, plan gate and templates. No warning. |
|
||||
|
||||
After run B the following was checked independently of the script's own report:
|
||||
|
||||
- **Gitea:** private, owner the organization, default branch `main`, contents `LICENSE` and `README.md`; push mirror to `https://github.com/TirSystem-BashScript/repofoundry-e2e-org.git`, interval `10m0s`, `sync_on_commit` true, `last_error` empty.
|
||||
- **GitHub:** private, owner the organization, contents `LICENSE` and `README.md`, one commit `Initial commit` (arrived through the mirror).
|
||||
- **Local project:** on `main` with that one commit as its whole history, tracking `origin`; `origin` is `ssh://git@git.tirsystem.com:10022/TirSystem-BashScript/repofoundry-e2e-org.git` and `github` is `https://github.com/TirSystem-BashScript/repofoundry-e2e-org.git`; the submodule `framework` comes from `ssh://git@git.tirsystem.com:10022/TirSystem/SQA-QC-Framework.git`; `core.hooksPath` is `framework/githooks` and `planGate.enabled` is true; skills are installed for both harnesses; `AGENTS.md` equals the framework template; no commit was made by the script.
|
||||
- **Hooks and gate, for real:** a commit on `main` was refused ("refusing to commit directly on 'main'"); a `src/` change without a `Task:` trailer on a branch was refused ("plan-first gate"); a documentation-only commit on a branch was accepted.
|
||||
- **Mirror direction, for real:** that commit was pushed to Gitea over SSH and the branch `work` appeared on GitHub within seconds, without a manual sync.
|
||||
|
||||
## Checklist Results (MIL-003 Go/No-Go)
|
||||
|
||||
| # | Criterion | Status | Evidence/Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | `git remote -v` shows `origin` (Gitea) and, when GitHub was chosen, `github`, with no credentials in any URL; with GitHub chosen the local history contains the license commit | Pass | Run B: configured addresses above, credential-free; history is the Gitea license commit. |
|
||||
| 2 | `framework` is a submodule of `ssh://git@git.tirsystem.com:10022/TirSystem/SQA-QC-Framework.git` and the install scripts have run once, in the documented order | Pass | Run B: `.gitmodules` holds that address; skills, then hooks, then templates; each once. |
|
||||
| 3 | An existing directory, `AGENTS.md` or `docs/artifact-registry.md` is never overwritten without a yes | Pass | Verified by the automated tests with real git (existing directory, conflicting `LICENSE`, existing templates); not repeated on the real hosts. |
|
||||
| 4 | With the plan gate enabled, a commit touching `src/` or `tests/` without a `Task: MIL-NNN#N` trailer is refused | Pass | Run B, for real. |
|
||||
| 5 | An existing `core.hooksPath` is reported and not replaced without consent | Pass | Verified by the automated tests with real git (local and global setting); not repeated on the real hosts. |
|
||||
| 6 | README covers installation, configuration, usage examples, security decisions, error handling and stakeholders, in clear English | N-A | The sections are written; the review by S02 has not happened yet (action item). |
|
||||
| 7 | End-to-end run on disposable repositories passes and the final review records no open security finding | Fail | No open security finding, and the organization-owner run passes on both hosts. The user-owner run could not be completed on Gitea (token scope). |
|
||||
| 8 | All acceptance criteria of US-001.03 in [US-001] are met | Pass | Run B: remotes without credentials, framework, skills, hooks, plan gate and templates in place; the "asks first" criterion by the automated tests. |
|
||||
|
||||
## Final security review
|
||||
|
||||
| Item | Result | Evidence |
|
||||
| --- | --- | --- |
|
||||
| Credential handling | No finding | Both tokens were searched for in every file of the new project, including the whole `.git` folder, and in all output of all runs: zero hits. Remote addresses and `.gitmodules` carry no credential. Tokens went to `curl` through a private configuration file and, for an HTTPS fetch, to git through `GIT_ASKPASS` and the environment (covered by tests; the live runs used SSH). |
|
||||
| Repository ownership | No finding | Created under the owner chosen at the prompt on both hosts (organization in run B; GitHub user in run A). `GITHUB_USER` was only a default. |
|
||||
| Mirror direction | No finding | Gitea is the source: a branch pushed to Gitea reached GitHub on its own. Nothing was pushed from GitHub; that direction was not tested. |
|
||||
| Submodule setup | No finding | Added over SSH on port 10022 from the configured framework repository; the SSH test and the host key check passed. |
|
||||
| API limitations | Findings F2 to F4 | `sync_on_commit` was applied on Gitea 1.27.3 (the upstream bug did not occur). Token scopes and the README Gitea adds are covered below. |
|
||||
| Residual risks | Accepted, documented | The mirror password (the GitHub token) is stored by the Gitea server. The tokens used here are broad (for example `admin:org` on GitHub); tokens limited to what the script needs would reduce the damage of a leak. |
|
||||
|
||||
## Findings
|
||||
|
||||
| # | Finding | Severity | Status |
|
||||
| --- | --- | --- | --- |
|
||||
| F1 | The real `ssh` used for the SSH test reads standard input and swallowed the answers meant for later prompts, so a run that was piped or pasted stopped before creating anything. The test stub did not read stdin, so no test could see it. | Defect (no data lost) | Fixed: stdin is closed for `ssh`, `git`, `curl` and the framework scripts; the test stub now reads stdin like the real tool; a regression test fails without the fix. |
|
||||
| F2 | Gitea adds a generated `README.md` next to the `LICENSE`. The script, the README and MIL-002 criterion 2 assumed only the license. A repository this script created was therefore counted as "has content" and could not be reused after a partial failure. | Defect | Fixed: `LICENSE` and `LICENSE` + `README.md` count as content created by the script; any other file still counts as content; tests added; README corrected. MIL-002 criterion 2 corrected (new `Proposed` version row, to be accepted). |
|
||||
| F3 | Creating a repository under one's own Gitea account needs the `write:user` scope, not `write:repository`. | Documentation | Fixed in the README, marked as confirmed by the server. |
|
||||
| F4 | Documentation had marked two scopes "not confirmed". Result: `write:user` is needed for user-owned Gitea repositories (F3); organization-owned repositories worked with `write:organization`, `write:repository` and `read:user`, and the minimum was not narrowed down; the GitHub membership check worked with `repo` and `admin:org`, `read:org` alone was not tested. | Documentation | README updated with what was observed. |
|
||||
|
||||
## Files created and external prerequisites
|
||||
|
||||
The script creates, in the new project: `.git`, `.gitmodules`, `framework/` (submodule), `.agents/skills/` and `.claude/skills/`, `AGENTS.md`, `docs/artifact-registry.md`, and (through the Gitea history) `LICENSE` and `README.md`. It never deletes anything.
|
||||
|
||||
External prerequisites: bash 4.4 or later, `git`, `curl`, `mktemp`; optional `jq` and `ssh`. An SSH key in the Gitea account and a known host key for `git.tirsystem.com` port 10022 (the script refuses unknown host keys). A GitHub token that can create repositories and push (classic `repo`), only when GitHub is chosen. A Gitea token with `write:repository`, `write:organization` and `read:user`, plus `write:user` for a repository under one's own account. Push mirrors must be enabled on the Gitea server.
|
||||
|
||||
## Disposable resources left in place (nothing was deleted)
|
||||
|
||||
- Gitea: `TirSystem-BashScript/repofoundry-e2e-org` (private; branches `main` and `work`).
|
||||
- GitHub: `TirSystem-BashScript/repofoundry-e2e-org` (private; branches `main` and `work`).
|
||||
- GitHub: `Tirsvad/repofoundry-e2e-user` (private, empty; created by run A).
|
||||
- A local temporary directory with the run output and the new project.
|
||||
|
||||
## Overall Verdict
|
||||
|
||||
Go-with-conditions — No open security finding, the organization-owner flow works end to end on both hosts, and the two defects the live run found are fixed with regression tests. Criterion 6 awaits the README review, and criterion 7 is not complete because the user-owner run could not finish on Gitea without `write:user`. Author and reviewer are the same person for now (S01 and S02 are both held by the Maintainer), so the framework independence rule is not met; re-review when a second person takes S02.
|
||||
|
||||
## Action Items
|
||||
|
||||
| Action | Owner | Due |
|
||||
| --- | --- | --- |
|
||||
| Create a Gitea token that also has `write:user` and repeat run A (the empty GitHub repository `Tirsvad/repofoundry-e2e-user` is offered for reuse) to complete criterion 7 | S01 | 2026-10-16 |
|
||||
| Review the README against criterion 6 | S02 | 2026-10-12 |
|
||||
| Accept the corrected criterion 2 of [MIL-002] (version row `Proposed`) | S02 | 2026-10-12 |
|
||||
| Delete the disposable repositories listed above in the web interfaces | S01 | 2026-10-12 |
|
||||
| Run `tests/run-tests.sh` on Linux and macOS (carried over from [RC-016]) | S02 | 2026-10-30 |
|
||||
| Consider tokens limited to what the script needs, for the Gitea and GitHub accounts used with it | S02 | 2026-10-30 |
|
||||
|
||||
---
|
||||
|
||||
[MIL-003]: ../../milestones/mil-003-scaffold-and-release.md
|
||||
[MIL-002]: ../../milestones/mil-002-repositories-and-mirror.md
|
||||
[RC-016]: ./rc-016-create-project-sh.md
|
||||
[US-001]: ../../user-stories.md
|
||||
@@ -47,7 +47,7 @@ run_framework_script() {
|
||||
abs="$(cd "$dir" && pwd)"
|
||||
make_temp_file
|
||||
out="$REPLY"
|
||||
if ! (cd "$abs" && env PROJECT_ROOT="$abs" bash "framework/scripts/$script" "$@") >"$out" 2>&1; then
|
||||
if ! (cd "$abs" && env PROJECT_ROOT="$abs" bash "framework/scripts/$script" "$@" </dev/null) >"$out" 2>&1; then
|
||||
die "the framework script $script failed: $(tail -n 3 "$out" | tr '\n' ' ')"
|
||||
fi
|
||||
}
|
||||
|
||||
+3
-2
@@ -6,13 +6,14 @@
|
||||
#
|
||||
# Provides: git_project, fetch_origin
|
||||
|
||||
# git_project DIR ARGS...: run git in DIR. Prompts are switched off, so a
|
||||
# git_project DIR ARGS...: run git in DIR. Prompts are switched off and stdin
|
||||
# is closed (git must not eat the answers meant for later prompts), so a
|
||||
# missing credential or SSH key fails at once instead of waiting for input.
|
||||
git_project() {
|
||||
local dir="$1"
|
||||
shift
|
||||
GIT_TERMINAL_PROMPT=0 GIT_SSH_COMMAND="${GIT_SSH_COMMAND:-ssh} -o BatchMode=yes" \
|
||||
git -C "$dir" "$@"
|
||||
git -C "$dir" "$@" </dev/null
|
||||
}
|
||||
|
||||
# fetch_origin DIR: fetch the Gitea repository into the project. Over SSH the
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ http_request() {
|
||||
# that never contains the request.
|
||||
HTTP_STATUS="$(curl --silent --max-time "$HTTP_TIMEOUT_SECONDS" \
|
||||
--connect-timeout 10 --output "$out_file" --write-out '%{http_code}' \
|
||||
--config "$config_file" "${data_args[@]}" 2>/dev/null)" || curl_status=$?
|
||||
--config "$config_file" "${data_args[@]}" </dev/null 2>/dev/null)" || curl_status=$?
|
||||
# The configuration file holds the token and the body may hold another one
|
||||
# (the mirror password): remove both now instead of at exit.
|
||||
rm -f -- "$config_file" ${body_file:+"$body_file"}
|
||||
|
||||
+10
-6
@@ -28,7 +28,8 @@ check_gitea_license() {
|
||||
}
|
||||
|
||||
# inspect_repository HOST: record in STATE[HOST_repo] whether the repository
|
||||
# is free (does not exist), empty, license_only or not_empty.
|
||||
# is free (does not exist), empty, initial_only (just the LICENSE and the
|
||||
# README.md Gitea adds) or not_empty.
|
||||
inspect_repository() {
|
||||
local host="$1" owner name names
|
||||
owner="$(repo_owner "$host")"
|
||||
@@ -45,10 +46,13 @@ inspect_repository() {
|
||||
return 0
|
||||
fi
|
||||
expect_status "cannot read the contents of the $(host_label "$host") repository $owner/$name" 200
|
||||
names="$(json_values "$HTTP_BODY_FILE" name)"
|
||||
case "$names" in
|
||||
# Gitea adds a README.md of its own next to the LICENSE when it creates a
|
||||
# repository with a license (seen on a real server), so both count as the
|
||||
# content this script creates.
|
||||
names="$(json_values "$HTTP_BODY_FILE" name | sort | tr '\n' ' ')"
|
||||
case "${names% }" in
|
||||
"") STATE[${host}_repo]="empty" ;;
|
||||
LICENSE) STATE[${host}_repo]="license_only" ;;
|
||||
"LICENSE" | "LICENSE README.md") STATE[${host}_repo]="initial_only" ;;
|
||||
*) STATE[${host}_repo]="not_empty" ;;
|
||||
esac
|
||||
}
|
||||
@@ -117,7 +121,7 @@ test_gitea_ssh() {
|
||||
return 0
|
||||
fi
|
||||
output="$(ssh -p "$port" -o BatchMode=yes -o ConnectTimeout=5 \
|
||||
-o StrictHostKeyChecking=yes -T "git@$host" 2>&1)" || status=$?
|
||||
-o StrictHostKeyChecking=yes -T "git@$host" </dev/null 2>&1)" || status=$?
|
||||
if ((status == 0)) || [[ $output == *"successfully authenticated"* ]]; then
|
||||
STATE[is_ssh_ok]=1
|
||||
STATE[ssh_note]="passed"
|
||||
@@ -138,7 +142,7 @@ decide_existing_repositories() {
|
||||
case "$state" in
|
||||
free) ;;
|
||||
empty) STATE[reuse_$host]=1 ;;
|
||||
license_only)
|
||||
initial_only)
|
||||
if [[ $host == gitea ]] && ((PROJECT[has_github])); then
|
||||
STATE[reuse_$host]=1
|
||||
else
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ report_outcome() {
|
||||
say "The project is in ${PROJECT[directory]}. Nothing was committed there: review it, then work on a branch."
|
||||
else
|
||||
say "To continue: fix the problem named above and run the same command again with --apply."
|
||||
say "A repository this run created is still empty (or holds only the license), so the next run offers to reuse it."
|
||||
say "A repository this run created is still empty (or holds only the license and the README Gitea adds), so the next run offers to reuse it."
|
||||
say "A directory, remotes and submodule created so far are used again by the next run; you are asked before an existing directory or file is touched."
|
||||
say "Nothing is deleted automatically. To start over, delete the repositories above in the web interface and the project directory by hand."
|
||||
fi
|
||||
|
||||
@@ -244,6 +244,9 @@ STUB
|
||||
write_ssh_stub() {
|
||||
cat >"$WORK/bin/ssh" <<STUB
|
||||
#!/usr/bin/env bash
|
||||
# The real ssh reads standard input until it ends; so does this stub. Whatever
|
||||
# is left on the caller's stdin (the answers to later prompts) is lost to it.
|
||||
cat >/dev/null
|
||||
printf '%s\n' "\$@" >>"\$STUB_DIR/ssh.args"
|
||||
if [[ ${1:-0} == 0 ]]; then
|
||||
echo "Hi there, gitea-user! You've successfully authenticated, but Gitea does not provide shell access."
|
||||
|
||||
+12
-1
@@ -158,10 +158,21 @@ test_example_files_hold_placeholders_only() {
|
||||
fail ".env.example has a value for ${line%%=*}; it must be empty"
|
||||
fi
|
||||
done < <(grep -E '^[A-Z_]+=' "$REPO_ROOT/.env.example")
|
||||
# The example holds a placeholder for the Gitea address, so it must be
|
||||
# edited before use: as it is, it is refused with a clear message...
|
||||
run_lib "" "parse_env_file \"$REPO_ROOT/config.env.example\" CONFIG_KEYS CONFIG
|
||||
validate_config
|
||||
echo parsed"
|
||||
assert_contains "config.env.example is valid" "$OUT" "parsed"
|
||||
assert_status "placeholder address is refused" 1 "$STATUS"
|
||||
assert_contains "names the key" "$ERR" "GITEA_URL"
|
||||
# ...and with a real address in its place the rest of the file is valid.
|
||||
sed -e 's|^GITEA_URL=.*|GITEA_URL=https://git.example.test/|' \
|
||||
-e 's|^GITEA_API_URL=.*|GITEA_API_URL=https://git.example.test/api/v1|' \
|
||||
"$REPO_ROOT/config.env.example" >"$WORK/example.env"
|
||||
run_lib "" "parse_env_file \"$WORK/example.env\" CONFIG_KEYS CONFIG
|
||||
validate_config
|
||||
echo parsed"
|
||||
assert_contains "config.env.example is valid once the address is set" "$OUT" "parsed"
|
||||
run_lib "" "parse_env_file \"$REPO_ROOT/.env.example\" CREDENTIAL_KEYS CREDENTIALS
|
||||
echo parsed"
|
||||
assert_contains ".env.example parses" "$OUT" "parsed"
|
||||
|
||||
@@ -476,3 +476,42 @@ mirror_field '$WORK/m.json' https://github.com/o/b.git sync_on_commit"
|
||||
assert_eq "the right mirror is chosen with jq" "true" "$OUT"
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------- found by the live e2e run
|
||||
|
||||
test_a_repository_this_script_created_earlier_can_be_reused() {
|
||||
# Seen on a real Gitea: creating a repository with a license also adds a
|
||||
# README.md. After a failed mirror step the next run must still reuse it.
|
||||
setup_hosts
|
||||
prepend_route 'GET|/api/v1/repos/TirSystem/my-app|200|{"empty":false}'
|
||||
prepend_route 'GET|/api/v1/repos/TirSystem/my-app/contents|200|[{"name":"README.md","type":"file"},{"name":"LICENSE","type":"file"}]'
|
||||
run_apply "$ANSWERS_GITHUB"$'y\ny\n'
|
||||
assert_status "LICENSE and README.md" 0 "$STATUS"
|
||||
assert_not_contains "not created again" "$(calls)" "$GITEA_REPO_CALL"
|
||||
assert_contains "reported" "$OUT" "Gitea repository : reused"
|
||||
}
|
||||
|
||||
test_other_files_still_count_as_content() {
|
||||
local listing
|
||||
for listing in '[{"name":"README.md","type":"file"}]' \
|
||||
'[{"name":"LICENSE","type":"file"},{"name":"README.md","type":"file"},{"name":"main.c","type":"file"}]' \
|
||||
'[{"name":"LICENSE","type":"file"},{"name":"src","type":"dir"}]'; do
|
||||
setup_hosts
|
||||
prepend_route 'GET|/api/v1/repos/TirSystem/my-app|200|{"empty":false}'
|
||||
prepend_route "GET|/api/v1/repos/TirSystem/my-app/contents|200|$listing"
|
||||
run_dry "$ANSWERS_GITHUB"
|
||||
assert_status "content: $listing" 1 "$STATUS"
|
||||
assert_contains "refused" "$ERR" "already exists and has content"
|
||||
done
|
||||
}
|
||||
|
||||
test_children_never_eat_the_answers_meant_for_later_prompts() {
|
||||
# The real ssh reads standard input until it ends; the stub does too. The
|
||||
# script closes stdin for ssh, git, curl and the framework scripts, so the
|
||||
# answers that follow the SSH test still reach the later prompts.
|
||||
setup_hosts
|
||||
run_apply "$ANSWERS_GITHUB"$'y\n'
|
||||
assert_status "the final question is still answered" 0 "$STATUS"
|
||||
assert_contains "created" "$OUT" "Done. This is what exists now:"
|
||||
assert_not_contains "no lost input" "$ERR" "no input available"
|
||||
}
|
||||
|
||||
+3
-3
@@ -217,13 +217,13 @@ test_an_existing_hooks_path_is_not_replaced_without_a_yes() {
|
||||
|
||||
test_a_global_hooks_path_is_reported_not_changed() {
|
||||
setup_hosts
|
||||
git config --file "$WORK/gitconfig" core.hooksPath /somewhere/global-hooks
|
||||
git config --file "$WORK/gitconfig" core.hooksPath global-hooks-dir
|
||||
local dir="$WORK/project"
|
||||
local_answers "$dir" y n
|
||||
run_apply "$LOCAL_ANSWERS"$'y\n'
|
||||
assert_status "apply" 0 "$STATUS"
|
||||
assert_contains "warns" "$ERR" "your global core.hooksPath is '/somewhere/global-hooks'"
|
||||
assert_eq "the global setting is untouched" "/somewhere/global-hooks" "$(git config --file "$WORK/gitconfig" --get core.hooksPath)"
|
||||
assert_contains "warns" "$ERR" "your global core.hooksPath is 'global-hooks-dir'"
|
||||
assert_eq "the global setting is untouched" "global-hooks-dir" "$(git config --file "$WORK/gitconfig" --get core.hooksPath)"
|
||||
assert_eq "the project has its own" "framework/githooks" "$(project_git "$dir" config --local --get core.hooksPath)"
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user