diff --git a/.github/workflows/close-dangling-prs.yml b/.github/workflows/close-dangling-prs.yml deleted file mode 100644 index 0524d901ce75..000000000000 --- a/.github/workflows/close-dangling-prs.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Close dangling PRs - -# **What it does**: Searches for specific PRs that have been open for a certain number of days and closes them. -# **Why we have it**: Tidying up so humans don't need to do it. -# **Who does it impact**: docs-engineering - -on: - workflow_dispatch: - schedule: - - cron: '20 16 * * *' # Run every day at 16:20 UTC / 8:20 PST - pull_request: - paths: - - .github/workflows/close-dangling-prs.yml - - src/workflows/close-dangling-prs.ts - -permissions: - contents: read - pull-requests: write - -jobs: - close-dangling-prs: - if: github.repository == 'github/docs-internal' - runs-on: ubuntu-latest - steps: - - name: Check out repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - uses: ./.github/actions/node-npm-setup - - - name: Close dangling PRs - env: - GITHUB_TOKEN: ${{ secrets.DOCS_BOT_PAT_READPUBLICKEY }} - DRY_RUN: ${{ github.event_name == 'pull_request'}} - run: npm run close-dangling-prs - - - uses: ./.github/actions/slack-alert - if: ${{ failure() && github.event_name == 'schedule' }} - with: - slack_channel_id: ${{ secrets.DOCS_ALERTS_SLACK_CHANNEL_ID }} - slack_token: ${{ secrets.SLACK_DOCS_BOT_TOKEN }} diff --git a/assets/images/help/copilot/copilot-button-for-file.png b/assets/images/help/copilot/copilot-button-for-file.png index 6319565dea22..af3232e42753 100644 Binary files a/assets/images/help/copilot/copilot-button-for-file.png and b/assets/images/help/copilot/copilot-button-for-file.png differ diff --git a/assets/images/help/copilot/factorial-finder-copilot-button.png b/assets/images/help/copilot/factorial-finder-copilot-button.png new file mode 100644 index 000000000000..461d5085a429 Binary files /dev/null and b/assets/images/help/copilot/factorial-finder-copilot-button.png differ diff --git a/assets/images/help/dependabot/learners-enable-dependabot.png b/assets/images/help/dependabot/learners-enable-dependabot.png new file mode 100644 index 000000000000..08a025c7700c Binary files /dev/null and b/assets/images/help/dependabot/learners-enable-dependabot.png differ diff --git a/assets/images/help/repository/license-info-python-factorial.png b/assets/images/help/repository/license-info-python-factorial.png new file mode 100644 index 000000000000..3b9476e7d74d Binary files /dev/null and b/assets/images/help/repository/license-info-python-factorial.png differ diff --git a/content/get-started/exploring-projects-on-github/index.md b/content/get-started/exploring-projects-on-github/index.md index 0d37c5176c6a..06e5bfba0de3 100644 --- a/content/get-started/exploring-projects-on-github/index.md +++ b/content/get-started/exploring-projects-on-github/index.md @@ -20,6 +20,5 @@ children: - /saving-repositories-with-stars - /following-people - /following-organizations - shortTitle: Explore projects --- diff --git a/content/get-started/index.md b/content/get-started/index.md index 7709cdd2f325..4a8fdcdda43d 100644 --- a/content/get-started/index.md +++ b/content/get-started/index.md @@ -53,6 +53,7 @@ children: - /onboarding - /using-github - /learning-about-github + - /learning-to-code - /accessibility - /writing-on-github - /exploring-projects-on-github diff --git a/content/get-started/learning-to-code/index.md b/content/get-started/learning-to-code/index.md new file mode 100644 index 000000000000..0a9c70bbfa2c --- /dev/null +++ b/content/get-started/learning-to-code/index.md @@ -0,0 +1,9 @@ +--- +title: Learning to code +intro: "Learn how {% data variables.product.github %} can help while you're learning to code." +versions: + fpt: '*' +children: + - /reusing-other-peoples-code-in-your-projects +shortTitle: Learn to code +--- diff --git a/content/get-started/learning-to-code/reusing-other-peoples-code-in-your-projects.md b/content/get-started/learning-to-code/reusing-other-peoples-code-in-your-projects.md new file mode 100644 index 000000000000..69d64196f0d5 --- /dev/null +++ b/content/get-started/learning-to-code/reusing-other-peoples-code-in-your-projects.md @@ -0,0 +1,148 @@ +--- +title: Reusing other people's code in your projects +intro: 'Increase your coding efficiency and knowledge by integrating existing code into your projects.' +shortTitle: Reuse people's code +versions: + fpt: '*' +allowTitleToDifferFromFilename: true +--- + +One of the best things about open source software is the ability to reuse other people's code. Repurposing code helps you save time, discover new functionality, and learn other programming styles. There are two main ways to reuse code: +* **Copying and pasting a code snippet directly into your project.** If you're new to coding, this is the quickest way to start reusing code. +* **Importing a library into your project.** While this approach takes some time to learn, it's ultimately easier and more efficient. It's also a foundational skill for software development. + +## Using other people's code snippets in your project + +As you are learning to code, you might copy and paste other people's code snippets into your project. While using code snippets is a great way to learn to code and save time, there are a few key steps you should always take before copying another developer's code. + +### 1. Finding and understanding a code snippet + +Your first step is to choose a code snippet you want to reuse. To find eligible code, try [searching {% data variables.product.github %}](https://github.com/search). You can narrow your search with search qualifiers like `language:YOUR-SEARCH-LANGUAGE`. + +For example, let's say you want to reuse Python code that calculates the factorial of a number. By searching {% data variables.product.github %} for `factorial language:python is:repository user:devwithdocs`, we find the [`devwithdocs/python-factorial`](https://github.com/devwithdocs/python-factorial) repository, which implements the calculator in [`factorial_finder.py`](https://github.com/devwithdocs/python-factorial/blob/main/factorial_finder.py) using a loop: + +```python +# Initialize the factorial result to 1 +factorial = 1 + +# Initialize the input number to 6 +number = 6 + +# Loop from 1 to number (inclusive) and multiply factorial by each number +for i in range(1, number + 1): + factorial *= i + +print(f"The factorial of {number} is {factorial}") +``` + +If you don't immediately understand a code snippet, you can ask {% data variables.product.prodname_copilot_short %} to explain it. In the menu bar at the top of the [`factorial_finder.py`](https://github.com/devwithdocs/python-factorial/blob/main/factorial_finder.py) file, click {% octicon "copilot" aria-label="Ask Copilot about this file" %} to start a conversation about the file, then ask {% data variables.product.prodname_copilot_short %} to `Explain this program`. + + ![Screenshot of the {% data variables.product.prodname_copilot_short %} button, outlined in dark orange, at the top of the file view.](/assets/images/help/copilot/factorial-finder-copilot-button.png) + +### 2. Understanding project licensing + +Before you can reuse the code you've found, you need to understand its licensing. Licenses determine how you can use the code in a project, including your ability to copy, modify, and distribute that code. + +You can find the license for a project in the "About" section of the main page of the repository. For example, we can see that the [`devwithdocs/python-factorial`](https://github.com/devwithdocs/python-factorial) repository is licensed under the MIT license. To read the license, click {% octicon "law" aria-hidden="true" %} **MIT license**. + + ![Screenshot of the main page of the devwithdocs/python-factorial repository. In the right sidebar, "MIT license" is outlined in dark orange.](/assets/images/help/repository/license-info-python-factorial.png) + +> [!NOTE] If the code you want to use is under a different license, use the license summaries in [Licenses](https://choosealicense.com/licenses/) to understand it. + +We want to copy the entire `factorial_finder.py` file, so the MIT license indicates that we should include a copy of the license in our own project. At the top of your Python file, paste the license as a comment. + +### 3. Using and modifying code snippets + +Now that you understand the code and its licensing, you can paste the code snippet into your project. While you may be able to use the code as is, you'll often need to modify it for your specific use case. + +For our factorial example, let's say we want to quickly calculate the factorials of 5, 7, 9, and 10. Instead of copying and pasting the entire program for each number, we can move our calculator into a function that takes a number as an argument. + +To help us make these changes, let's [ask {% data variables.product.prodname_copilot_short %}](https://github.com/copilot) to suggest and explain an implementation. We can paste our current code into the chat window, then prompt {% data variables.product.prodname_copilot_short %} to `Wrap the Python code above in a function`. {% data variables.product.prodname_copilot_short %} will generate code that looks something like this: + +```python copy +def calculate_factorial(number): + # Initialize the factorial result to 1 + factorial = 1 + + # Loop from 1 to number (inclusive) and multiply factorial by each number + for i in range(1, number + 1): + factorial *= i + + return factorial +``` + +With our new function, we can easily find the factorials of our numbers by adding the following code to our project, then running the Python program: + +```python copy +print(calculate_factorial(5)) +print(calculate_factorial(7)) +print(calculate_factorial(9)) +print(calculate_factorial(10)) +``` + +Congratulations! You've successfully found, understood, and modified an example code snippet. + +## Using code from libraries in your project + +Using libraries is standard practice for developers. Libraries are essentially collections of code written by other developers to perform specific tasks. You can import libraries into your project to use the pre-written code, saving you time and effort. + +In this section, we'll continue working with the Python factorial calculator example from the previous section. For reference, here's our current code: + +```python copy +def calculate_factorial(number): + # Initialize the factorial result to 1 + factorial = 1 + + # Loop from 1 to number (inclusive) and multiply factorial by each number + for i in range(1, number + 1): + factorial *= i + + return factorial + +print(calculate_factorial(5)) +print(calculate_factorial(7)) +print(calculate_factorial(9)) +print(calculate_factorial(10)) +``` + +### 1. Finding a library + +Once you know what functionality you want to add to your project, you can search for a library with relevant code. {% data variables.product.prodname_copilot_chat_short %} is an easy way to search for libraries, since you can use natural language to describe exactly what you're looking for. + +Returning to our factorial calculator, finding a factorial is a pretty common function, and there's a good chance someone included that function in an existing library. If we [ask {% data variables.product.prodname_copilot_short %}](https://github.com/copilot) `Is there a Python library with a function for calculating a factorial?`, {% data variables.product.prodname_copilot_short %} will tell us a factorial function is included in the [`math`](https://docs.python.org/3/library/math.html) module from the standard Python library. + +### 2. Prioritizing security in your project + +When you add a library or module to your project, you create what's called a dependency. Dependencies are pre-written code bundles that your project relies on to function correctly, and if they aren't carefully written or maintained, they can introduce security vulnerabilities to your work. Thankfully, there are some steps you can take to best protect your project. + +#### Using popular libraries + +Popular libraries are more likely to be secure because they are actively maintained and used by many developers. One good marker of popularity is the number of stars a repository has. If you can't find the {% data variables.product.github %} repository for a dependency, try [asking {% data variables.product.prodname_copilot_short %}](https://github.com/copilot) for help. + +For example, for the `math` module, we can prompt {% data variables.product.prodname_copilot_short %} to `Find the GitHub repository containing the code for the math module in Python`. {% data variables.product.prodname_copilot_short %} will tell us that the `math` module is defined in [`python/cpython`](https://github.com/python/cpython), which has over 64,000 stars. + +#### Enabling {% data variables.product.prodname_dependabot_alerts %} for your project + +If enabled, {% data variables.product.prodname_dependabot_alerts %} are automatically generated when {% data variables.product.prodname_dependabot %} detects a security issue in your dependencies, helping you quickly fix vulnerabilities. {% data variables.product.prodname_dependabot %} is available for free on all open source {% data variables.product.github %} repositories. + +To turn {% data variables.product.prodname_dependabot_alerts %} on, click the **Security** tab for your project's {% data variables.product.github %} repository. Next to {% data variables.product.prodname_dependabot_alerts %}, click **Enable {% data variables.product.prodname_dependabot_alerts %}**. You can access {% data variables.product.prodname_dependabot_alerts %} from the **{% data variables.product.prodname_dependabot %}** tab of the sidebar. + + ![Screenshot of the "Security" page of a repository. The "Security" tab, "{% data variables.product.prodname_dependabot %}" tab, and "Enable {% data variables.product.prodname_dependabot_alerts %}" button are all outlined in dark orange.](/assets/images/help/dependabot/learners-enable-dependabot.png) + +### 3. Implementing code from a library + +Now that you have taken steps to secure your work, you need to import the library into your project, then use its contents in your code. You can read the documentation for the library to learn how to do it yourself, or you can ask {% data variables.product.prodname_copilot_short %} to suggest and explain an implementation for you. + +For our factorial program, we can [ask {% data variables.product.prodname_copilot_short %}](https://github.com/copilot) something like `How do I use the factorial function of the math module in my Python project?` {% data variables.product.prodname_copilot_short %} will then suggest a version of the following code: + +```python copy +import math + +# Calculate the factorial of a number +number = 5 +result = math.factorial(number) + +print(f"The factorial of {number} is {result}") +``` + +After you replace the existing code in your project with the above implementation, you've successfully reused code from a library in your example project! diff --git a/package.json b/package.json index 7bbe34a03665..15f3c9f94664 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "build": "next build", "check-content-type": "tsx src/workflows/check-content-type.ts", "check-github-github-links": "tsx src/links/scripts/check-github-github-links.ts", - "close-dangling-prs": "tsx src/workflows/close-dangling-prs.ts", "cmp-files": "tsx src/workflows/cmp-files.ts", "content-changes-table-comment": "tsx src/workflows/content-changes-table-comment.ts", "copy-fixture-data": "tsx src/tests/scripts/copy-fixture-data.js", diff --git a/src/workflows/close-dangling-prs.ts b/src/workflows/close-dangling-prs.ts deleted file mode 100644 index 43ced3d4a2d3..000000000000 --- a/src/workflows/close-dangling-prs.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { program, Option } from 'commander' - -import github from './github.js' -import { octoSecondaryRatelimitRetry } from './secondary-ratelimit-retry' - -const DRY_RUN = process.env.DRY_RUN || 'false' -const DEFAULT_MIN_DAYS = 10 - -const CLOSING_COMMENT = `Closing this PR because it's been open for too long without any activity. - -This is an automated event triggered by \`close-danging-prs\`. -` - -// This script is deliberately vague in that we might have multiple -// of what a "dangling PR" is. You can search PRs by title and find -// some that should match and some that shouldn't. Because the title -// search isn't verbatim. -// The `titleSearch` finds a "shortlist" of candidates but then when -// we loop over the more carefully, we look for more specifics (verbatim) -// matchings on the PR title. -type Config = { - titleSearch: string - titlePrefix: string - userLogin?: string -} -const CONFIGS: Config[] = [ - { - titleSearch: 'FOR PREVIEW ONLY', - titlePrefix: '[FOR PREVIEW ONLY] Preview of OpenAPI', - userLogin: 'docs-bot', - }, -] - -const owner = process.env.GITHUB_REPOSITORY_OWNER || 'github' -const repo = process.env.GITHUB_REPOSITORY?.split('/')[1] || 'docs-internal' - -program - .description('Finds PRs with "FOR PREVIEW ONLY" in the title that are too old and closes them.') - .option('--owner', 'Owner of the repository', owner) - .option('--repo', 'Name of the repository', repo) - .addOption( - new Option( - '--dry-run', - "Don't post any comment or close any PRs. Only print what it would do.", - ).default(Boolean(JSON.parse(DRY_RUN))), - ) - .option( - '--min-days ', - 'Minimum number of days since creation to be considered.', - `${DEFAULT_MIN_DAYS}`, - ) - .action(main) - -program.parse(process.argv) - -type Options = { - owner: string - repo: string - dryRun: boolean - minDays: number | string -} - -async function main(options: Options) { - const { dryRun, owner, repo } = options - - const minDays = - typeof options.minDays === 'string' ? parseInt(options.minDays, 10) : options.minDays - - if (!process.env.GITHUB_TOKEN) { - throw new Error( - `GITHUB_TOKEN is required to run this action and it needs read-write access to ${owner}/${repo}`, - ) - } - - const octokit = github() - - for (const config of CONFIGS) { - let q = `repo:${owner}/${repo} is:pr is:open ` - if (config.titleSearch) q += `in:title "${config.titleSearch}" ` - if (config.userLogin) q += `author:${config.userLogin} ` - q = q.trim() - - const { data } = await octoSecondaryRatelimitRetry(() => - octokit.rest.search.issuesAndPullRequests({ q }), - ) - - type PRData = { - updated_at: string - } - data.items.sort( - (pr1: PRData, pr2: PRData) => - new Date(pr1.updated_at).getTime() - new Date(pr2.updated_at).getTime(), - ) - - let countTooOld = 0 - for (const pr of data.items) { - if (config.titlePrefix && !pr.title.startsWith(config.titlePrefix)) { - console.log("PR doesn't have the right title prefix. Skipping.") - continue - } - - const L = (label: string) => `${label}:`.padEnd(20) - console.log(L('URL'), pr.html_url) - console.log(L('UPDATED'), pr.updated_at) - console.log(L('CREATED'), pr.created_at) - console.log(L('STATE'), pr.state) - console.log(L('DRAFT'), pr.draft) - const ageDays = (Date.now() - new Date(pr.updated_at).getTime()) / (1000 * 60 * 60 * 24) - console.log(L('AGE (days)'), Math.floor(ageDays)) - if (ageDays < minDays) { - console.log('Not old enough. Leave as is.') - } else { - countTooOld++ - - if (!dryRun) { - console.log('Updating PR with a comment...') - await octokit.rest.issues.createComment({ - owner, - repo, - issue_number: pr.number, - body: CLOSING_COMMENT, - }) - - console.log('Closing PR...') - await octokit.rest.pulls.update({ - owner, - repo, - pull_number: pr.number, - state: 'closed', - }) - - console.log('** Closed and commented because it was old enough.**') - } else { - console.warn("** Would close and comment on PR if it wasn't a dry run. **") - } - } - console.log('\n') // break between each print - } - - console.log(`${data.items.length} PRs found, ${countTooOld} old enough to close.`) - } -}