{"id":50719590,"url":"https://github.com/obcode/generate_startercode","last_synced_at":"2026-06-09T22:30:33.365Z","repository":{"id":359282910,"uuid":"1245350908","full_name":"obcode/generate_startercode","owner":"obcode","description":"Generate Startercode and Solutions on GitLab for my courses","archived":false,"fork":false,"pushed_at":"2026-05-30T16:04:26.000Z","size":117,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-30T16:13:16.646Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/obcode.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-21T06:26:11.000Z","updated_at":"2026-05-30T16:04:28.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/obcode/generate_startercode","commit_stats":null,"previous_names":["obcode/generate_startercode"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/obcode/generate_startercode","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obcode%2Fgenerate_startercode","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obcode%2Fgenerate_startercode/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obcode%2Fgenerate_startercode/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obcode%2Fgenerate_startercode/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/obcode","download_url":"https://codeload.github.com/obcode/generate_startercode/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obcode%2Fgenerate_startercode/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34129072,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-09T22:30:32.753Z","updated_at":"2026-06-09T22:30:33.358Z","avatar_url":"https://github.com/obcode.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# generate_startercode\n\nAutomatic generation of startercode and solution branches from a shared main branch.\n\nThis repository is now structured as a uv project, includes tests, and uses GitHub Actions for pre-commit, tests, and automatic releases with Semantic Release.\n\n## Installation (uv)\n\n1. Install uv: https://docs.astral.sh/uv/\n2. Install dependencies:\n\n```bash\nuv sync --extra dev\n```\n\n3. Optionally install pre-commit hooks:\n\n```bash\nuv run pre-commit install --install-hooks\n```\n\n## Local Usage\n\n### transform.py\n\n```bash\nuv run python transform.py --target solution --repo-root /path/to/repo\nuv run python transform.py --target startercode --repo-root /path/to/repo\n```\n\n### sync_issue.py\n\n```bash\nuv run python sync_issue.py\n```\n\nNote: sync_issue.py expects GitLab environment variables, especially GITLAB_TOKEN, CI_SERVER_URL, and CI_PROJECT_PATH.\n\n## Download Latest Release Version\n\nYou can download the scripts from the latest GitHub release tag (instead of main):\n\n```bash\nLATEST_TAG=$(curl -fsSL https://api.github.com/repos/obcode/generate_startercode/releases/latest | python3 -c \"import json,sys; print(json.load(sys.stdin)['tag_name'])\")\nexport GENERATE_STARTERCODE_VERSION=\"${LATEST_TAG#v}\"\n\ncurl -fsSL \"https://raw.githubusercontent.com/obcode/generate_startercode/${LATEST_TAG}/transform.py\" -o /tmp/transform.py\ncurl -fsSL \"https://raw.githubusercontent.com/obcode/generate_startercode/${LATEST_TAG}/sync_issue.py\" -o /tmp/sync_issue.py\n```\n\n## Replacement for Existing GitLab CI Usage\n\nIf your GitLab project currently downloads directly from main, replace that with a download from the latest GitHub release tag.\n\nThe following .gitlab-ci configuration is a direct replacement for your previous flow:\n\n```yaml\n# -- Sync issue ------------------------------------------------\nsync-issue:\n  stage: sync\n  image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/library/python:3.12-bookworm\n  script:\n    - set -eu\n    - pip install \"python-gitlab[graphql]\" --quiet\n    - LATEST_TAG=$(curl -fsSL https://api.github.com/repos/obcode/generate_startercode/releases/latest | python3 -c \"import json,sys; print(json.load(sys.stdin)['tag_name'])\")\n    - export GENERATE_STARTERCODE_VERSION=\"${LATEST_TAG#v}\"\n    - echo \"[sync-issue] Using release ${LATEST_TAG}\"\n    - curl -fsSL \"https://raw.githubusercontent.com/obcode/generate_startercode/${LATEST_TAG}/sync_issue.py\" -o /tmp/sync_issue.py\n    - python /tmp/sync_issue.py\n  rules:\n    - if: '$CI_COMMIT_BRANCH == \"main\"'\n      changes:\n        - Aufgabenstellung/*\n        - .gitlab/ci/teacher.yml\n        - .gitlab-ci.yml\n\n# -- Generate branches -----------------------------------------\npublish-branches:\n  stage: publish\n  image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/library/python:3.12-bookworm\n  before_script:\n    - set -eu\n    - pip install pyyaml --quiet\n    - git config user.email \"ci@gitlab\"\n    - git config user.name \"CI\"\n    - git remote set-url origin \"https://oauth2:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git\"\n  script:\n    - LATEST_TAG=$(curl -fsSL https://api.github.com/repos/obcode/generate_startercode/releases/latest | python3 -c \"import json,sys; print(json.load(sys.stdin)['tag_name'])\")\n    - export GENERATE_STARTERCODE_VERSION=\"${LATEST_TAG#v}\"\n    - echo \"[publish-branches] Download transform.py from release ${LATEST_TAG}\"\n    - curl -fsSL \"https://raw.githubusercontent.com/obcode/generate_startercode/${LATEST_TAG}/transform.py\" -o /tmp/transform.py\n    - echo \"[publish-branches] Run target=solution\"\n    - python /tmp/transform.py --target solution --repo-root \"$CI_PROJECT_DIR\" --config .gitlab/ci/config.yml --no-skip-ci\n    - echo \"[publish-branches] Run target=startercode\"\n    - python /tmp/transform.py --target startercode --repo-root \"$CI_PROJECT_DIR\" --config .gitlab/ci/config.yml --no-skip-ci\n  rules:\n    - if: '$CI_COMMIT_BRANCH == \"main\"'\n      changes:\n        - \"src/**/*.py\"\n        - \"tests/**/*.py\"\n        - \"src/**/*.go\"\n        - .gitlab/ci/config.yml\n        - .gitlab/ci/teacher.yml\n        - .gitlab-ci.yml\n```\n\nNote: At least one GitHub release must exist. The additional environment variable GENERATE_STARTERCODE_VERSION ensures that the downloaded standalone script reports the correct release version even without a local pyproject. If no release exists yet, initially load once from a fixed tag (for example v1.0.0) or temporarily from main.\n\n## Configuration: .gitlab/ci/config.yml\n\nThe file `.gitlab/ci/config.yml` controls what is included per target (solution/startercode) in generated branches. Code transformations (SOLUTION_BEGIN/END markers) are defined directly in source code. This config only controls path removals, file patches, and postprocess commands.\n\n### Complete Example\n\n```yaml\n# .gitlab/ci/config.yml\n\nsolution:\n  remove_paths:\n    - Aufgabenstellung\n    - .gitlab/ci\n  patch_files:\n    .gitlab-ci.yml:\n      remove_line_containing:\n        - \".gitlab/ci/teacher.yml\"\n        - \"include:\"\n  postprocess_commands:\n    - uvx ruff check --select I --fix .\n    - uvx ruff format .\n\nstartercode:\n  remove_paths:\n    - Aufgabenstellung\n    - .gitlab/ci\n  patch_files:\n    .gitlab-ci.yml:\n      remove_line_containing:\n        - \".gitlab/ci/teacher.yml\"\n        - \"include:\"\n  postprocess_commands:\n    - uvx ruff check --select I --fix .\n    - uvx ruff format .\n```\n\n### remove_paths\n\nList of paths (files or directories) that are fully removed in the generated branch. Paths are relative to the repository root.\n\n```yaml\nsolution:\n  remove_paths:\n    - Aufgabenstellung        # full directory\n    - .gitlab/ci              # subdirectory\n    - src/secret_tests.py     # single file\n```\n\n### patch_files\n\nAllows line-based modifications for selected files in the generated branch. Currently supported operation: `remove_line_containing` - removes all lines containing the given substring.\n\n```yaml\nsolution:\n  patch_files:\n    .gitlab-ci.yml:\n      remove_line_containing:\n        - \".gitlab/ci/teacher.yml\"   # removes every line containing this string\n        - \"include:\"\n    README.md:\n      remove_line_containing:\n        - \"SOLUTION_ONLY\"\n```\n\n### postprocess_commands\n\nShell commands executed in the generated tree after all other transformations and before committing the branch. Useful for running formatters or linters after marker removal.\n\n**Python/Ruff:**\n\n```yaml\nsolution:\n  postprocess_commands:\n    - uvx ruff check --select I --fix .\n    - uvx ruff format .\n```\n\n**Go:**\n\n```yaml\nsolution:\n  postprocess_commands:\n    - find . -name \"*.go\" -exec goimports -w {} +\n    - gofmt -w .\n```\n\nNote: `goimports` requires `go install golang.org/x/tools/cmd/goimports@latest` in the CI environment. If you only need formatting, `gofmt` is enough.\n\n## GitHub Actions\n\n### CI Workflow\n\nFile: .github/workflows/ci.yml\n\nRuns on push to main and on pull requests:\n\n- pre-commit on all files\n- pytest test suite\n- Semantic Release dry-run on pull requests (`--print`)\n- Semantic Release on main, but only if pre-commit and tests succeed\n\n### Release Workflow\n\nFile: .github/workflows/release.yml\n\nOptional manual workflow (`workflow_dispatch`) for release experiments.\n\n## Versioning Without Hardcoded Single Values\n\nScripts no longer read their version from a manually maintained fixed value.\nInstead, the version is resolved from package metadata or pyproject.toml.\nThe version in pyproject.toml is managed by Semantic Release.\n\n## Tests\n\nTests are located under tests/ and can be run locally with uv:\n\n```bash\nuv run pytest\n```\n\n## Pre-commit\n\nThe existing .pre-commit-config.yaml remains active and is executed in CI.\n\nRun manually:\n\n```bash\nuv run pre-commit run --all-files\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobcode%2Fgenerate_startercode","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fobcode%2Fgenerate_startercode","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobcode%2Fgenerate_startercode/lists"}