diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..1b3343f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,72 @@ +name: Bug report +description: Report something that is broken or behaves incorrectly +title: "[Bug]: " +labels: ["type:bug", "status:needs-triage"] +body: + - type: markdown + id: intro + attributes: + value: | + Thanks for reporting a bug. Before you open a new issue, please search the existing issues to check whether it has already been reported. + + Do not include tokens, secrets, private repository names or email addresses in this issue. Replace them with placeholders. + + Security vulnerabilities must not be reported in public issues. Contact the maintainers privately instead. + - type: input + id: command + attributes: + label: Command or workflow + placeholder: e.g. showcase.py banner + validations: + required: false + - type: textarea + id: what-happened + attributes: + label: What happened? + description: Describe the problem you observed. + validations: + required: true + - type: textarea + id: expected + attributes: + label: What did you expect? + description: Describe the behaviour you expected instead. + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: Steps to reproduce + description: List the smallest set of steps that reproduces the problem. + placeholder: | + 1. ... + 2. ... + 3. ... + validations: + required: true + - type: input + id: version + attributes: + label: Version or commit + validations: + required: false + - type: dropdown + id: platform + attributes: + label: Platform + options: + - Windows + - macOS + - Linux + - GitHub Actions + - Other + validations: + required: false + - type: textarea + id: logs + attributes: + label: Output or logs + description: Paste relevant output or logs. Remove tokens and private details first. + render: text + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..308bd86 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Read the onboarding guide + url: https://github.com/CompleteTech-Skills/github-showcase-skill/blob/main/ONBOARDING.md + about: Setup and usage questions are usually answered in the onboarding guide. Never report a vulnerability in a public issue. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..bc42a52 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,53 @@ +name: Feature request +description: Suggest a new capability or enhancement +title: "[Feature]: " +labels: ["type:feature", "status:needs-triage"] +body: + - type: markdown + attributes: + value: | + Thanks for suggesting a feature. Before you open a new request, please search the existing issues to check whether it has already been proposed. If it has, add your context to that issue instead. + + Please do not include tokens, private names, private repository names or email addresses in this form. + - type: textarea + id: problem + attributes: + label: What problem would this solve? + description: Describe the situation or need that is not covered today. + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposed solution + description: Describe the capability or change you would like to see. + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Describe any workarounds or other approaches you have considered. + validations: + required: false + - type: dropdown + id: area + attributes: + label: Area + description: Which part of the showcase does this feature mostly touch? + options: + - Banner + - Card + - README + - Content + - Org or project + - Labels + - Docs + - Other + - type: textarea + id: context + attributes: + label: Additional context + description: Add any other context, links or examples that help explain the request. + validations: + required: false diff --git a/CHANGELOG.md b/CHANGELOG.md index cd69259..cb20df7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to `github-showcase-skill` are recorded here. Versions follo ### Added +- This repository now bundles its own presentation files: `assets/social-preview.png` (the accepted render, byte-identical, 1280x640, sha256 prefix `5d318b92`; not uploaded), `CODE_OF_CONDUCT.md` (Contributor Covenant 2.1, contact `Timothy.Gregg@complete.tech`), the issue forms `.github/ISSUE_TEMPLATE/bug_report.yml` and `feature_request.yml` plus `config.yml` using only existing standard labels, and the guard test `tests/test_own_presentation.py`, which also carries a Code of Conduct fidelity regression: the canonical Contributor Covenant 2.1 pledge including `caste, color` and a whitespace-normalized digest pin of the full text with only the documented contact substituted (#134). - This repository now bundles its own `assets/icon.png` (the normalized Codex-generated icon, provenance in `assets/PROMPTS.md`) and persists `chips.key` (`Claude + Codex`) and `chips.attrs` (`Python`, `Offline`) in `.github/showcase.yml`, so the banner and the card both take the same configured chips through `chips.derive` and the topic-derived `Claude ยท Codex` runtime chip is not repeated; a synthetic no-network regression test covers it. The README banner and card are regenerated from the committed configuration and icon (`banner.png` and `card.svg` are byte-identical to the visually reviewed render), and the icon provenance records the exact prompt, tool and hashes (#132). - The pre-#61 override compatibility test now uses a minimal synthetic legacy art-direction fixture (`tests/fixtures/content/ART_DIRECTION.synthetic-legacy.md`, placeholder names and neutral colours, no production brand data) instead of a copy of the previous bundled template; the original stays in git history. - `content icon prompt` takes `--seed N` to request an image seed. The prompt states it only when asked, and the markdown provenance line `Requested: 1 image; seed: ...` records `N` or `not supported` (#63). An invalid or missing glyph `size_class` stays `needs-writer` and the result (`note`, `invalid`) lists the allowed values WIDE, TALL, SQUARE and DIAGONAL with a one-line meaning each; the values are listed in `SKILL.md`, `references/content.md` and `references/art-direction.md` (#62). diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..bd1faf1 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,132 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +Timothy.Gregg@complete.tech. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/assets/social-preview.png b/assets/social-preview.png new file mode 100644 index 0000000..1e2b56d Binary files /dev/null and b/assets/social-preview.png differ diff --git a/tests/test_own_presentation.py b/tests/test_own_presentation.py new file mode 100644 index 0000000..9a46fe7 --- /dev/null +++ b/tests/test_own_presentation.py @@ -0,0 +1,131 @@ +"""Checks for the repository's own presentation assets, issue forms and code of conduct (#134). + +Stdlib plus PyYAML only. No network, no subprocess, no conftest fixtures. +""" + +import hashlib +import re +import struct +from pathlib import Path + +import yaml + +ROOT = Path(__file__).resolve().parents[1] + +SOCIAL_PREVIEW = ROOT / "assets" / "social-preview.png" +SOCIAL_PREVIEW_SHA256 = "5d318b92b04518de4a86af0429ef864a97390f6e4cc245a9d7cf78b18a433152" +PNG_SIGNATURE = b"\x89PNG\r\n\x1a\n" + +ISSUE_TEMPLATE_DIR = ROOT / ".github" / "ISSUE_TEMPLATE" +BUG_REPORT = ISSUE_TEMPLATE_DIR / "bug_report.yml" +FEATURE_REQUEST = ISSUE_TEMPLATE_DIR / "feature_request.yml" +CONFIG = ISSUE_TEMPLATE_DIR / "config.yml" +LABELS_STANDARD = ROOT / "references" / "standard" / "labels.yml" +CODE_OF_CONDUCT = ROOT / "CODE_OF_CONDUCT.md" + +BODY_TYPES = {"markdown", "input", "textarea", "dropdown", "checkboxes"} +EXPECTED_FORM_LABELS = { + "bug_report": ["type:bug", "status:needs-triage"], + "feature_request": ["type:feature", "status:needs-triage"], +} +CONTACT = "Timothy.Gregg@complete.tech" +EMAIL_PATTERN = r"[\w.+-]+@[\w-]+(?:\.[\w-]+)+" +COC_NORMALIZED_SHA256 = "89ac6173cceeb68d6217e3d745f12e6552cadbb48b9e88435a8d4ee8a0418816" + + +def _load_yaml(path): + return yaml.safe_load(path.read_text(encoding="utf-8")) + + +def _normalized(text): + return " ".join(text.split()) + + +def _documented_label_names(): + entries = _load_yaml(LABELS_STANDARD) + assert isinstance(entries, list) + return {entry["name"] for entry in entries} + + +def test_social_preview_dimensions(): + data = SOCIAL_PREVIEW.read_bytes() + assert data[:8] == PNG_SIGNATURE + width, height = struct.unpack(">II", data[16:24]) + assert (width, height) == (1280, 640) + + +def test_social_preview_hash_pinned(): + digest = hashlib.sha256(SOCIAL_PREVIEW.read_bytes()).hexdigest() + assert digest == SOCIAL_PREVIEW_SHA256 + + +def test_forms_parse_and_have_required_keys(): + for form_name, path in (("bug_report", BUG_REPORT), ("feature_request", FEATURE_REQUEST)): + form = _load_yaml(path) + assert isinstance(form, dict), path + for key in ("name", "description", "title"): + assert isinstance(form.get(key), str) and form[key].strip(), (path, key) + assert isinstance(form.get("labels"), list), path + assert form["labels"] == EXPECTED_FORM_LABELS[form_name], path + body = form.get("body") + assert isinstance(body, list) and body, path + seen_ids = set() + for element in body: + assert isinstance(element, dict), path + assert element.get("type") in BODY_TYPES, (path, element.get("type")) + if element["type"] == "markdown": + continue + element_id = element.get("id") + assert isinstance(element_id, str) and element_id, (path, element) + assert element_id not in seen_ids, (path, element_id) + seen_ids.add(element_id) + + +def test_issue_form_labels_exist_in_standard(): + documented = _documented_label_names() + for path in (BUG_REPORT, FEATURE_REQUEST): + form = _load_yaml(path) + for label in form["labels"]: + assert label in documented, (path, label) + config = _load_yaml(CONFIG) + assert "labels" not in config + + +def test_config_has_no_blank_issues_and_no_email(): + config = _load_yaml(CONFIG) + assert config["blank_issues_enabled"] is False + contact_links = config["contact_links"] + assert isinstance(contact_links, list) and contact_links + for link in contact_links: + assert link["url"].startswith("https://github.com/"), link + for path in (CONFIG, BUG_REPORT, FEATURE_REQUEST): + assert "@" not in path.read_text(encoding="utf-8"), path + + +def test_code_of_conduct_names_documented_contact(): + text = CODE_OF_CONDUCT.read_text(encoding="utf-8") + assert "Contributor Covenant" in text + assert "version 2.1" in text + assert CONTACT in text + assert set(re.findall(EMAIL_PATTERN, text)) == {CONTACT} + + +def test_code_of_conduct_pledge_has_canonical_tokens(): + """The pledge must match the Contributor Covenant 2.1 canonical text (#134). + + The official pledge lists "caste, color," after "race," and before "religion,". + The earlier omission of those tokens must fail this test. + """ + text = CODE_OF_CONDUCT.read_text(encoding="utf-8") + normalized = _normalized(text) + assert "personal appearance, race, caste, color, religion, or sexual identity and orientation." in normalized + assert "race, religion" not in normalized + + +def test_code_of_conduct_matches_canonical_text_digest(): + text = CODE_OF_CONDUCT.read_text(encoding="utf-8") + digest = hashlib.sha256(_normalized(text).encode("utf-8")).hexdigest() + assert digest == COC_NORMALIZED_SHA256, ( + "CODE_OF_CONDUCT.md must equal the canonical Contributor Covenant 2.1 text " + "(whitespace-normalized) with only the documented contact substituted" + )