From 5e057cdde90a00fcc68bdebf6c48fefb3c3fbd77 Mon Sep 17 00:00:00 2001 From: Joe <4088382+JoeStech@users.noreply.github.com> Date: Fri, 7 Feb 2025 23:39:19 -0700 Subject: [PATCH 01/15] initial files for copilot deployment lp --- assets/contributors.csv | 58 +++++++++++++++++- .../1-cdk-installation.md | 21 +++++++ .../2-cdk-services.md | 13 ++++ .../3-cdk-deployment.md | 0 .../4-github-config.md | 0 .../copilot-extension-deployment/_index.md | 43 +++++++++++++ .../_next-steps.md | 27 ++++++++ .../example-picture.png | Bin 0 -> 63167 bytes 8 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md create mode 100644 content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md create mode 100644 content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-cdk-deployment.md create mode 100644 content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md create mode 100644 content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md create mode 100644 content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_next-steps.md create mode 100644 content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/example-picture.png diff --git a/assets/contributors.csv b/assets/contributors.csv index 52c62fb06f..d6ddf26671 100644 --- a/assets/contributors.csv +++ b/assets/contributors.csv @@ -1,3 +1,4 @@ +<<<<<<< Updated upstream author,company,github,linkedin,twitter,website Jason Andrews,Arm,jasonrandrews,jason-andrews-7b05a8,, Pareena Verma,Arm,pareenaverma,pareena-verma-7853607,, @@ -78,4 +79,59 @@ Masoud Koleini,,,,, Na Li,Arm,,,, Tom Pilar,,,,, Cyril Rohr,,,,, -Odin Shen,Arm,,,, \ No newline at end of file +Odin Shen,Arm,,,, +======= +author,company,github,linkedin,twitter,website +Jason Andrews,Arm,jasonrandrews,jason-andrews-7b05a8,, +Pareena Verma,Arm,pareenaverma,pareena-verma-7853607,, +Ronan Synnott,Arm,,ronansynnott,, +Florent Lebeau,Arm,,,, +Brenda Strech,Remote.It,bstrech,bstrech,@remote_it,www.remote.it +Liliya Wu,Arm,Liliyaw,liliya-wu-8b6227216,, +Julio Suarez,Arm,jsrz,juliosuarez,, +Gabriel Peterson,Arm,gabrieldpeterson,gabrieldpeterson,@gabedpeterson,https://corteximplant.com/@gabe +Christopher Seidl,Arm,,,, +Michael Hall,Arm,,,, +Kasper Mecklenburg,Arm,,,, +Mathias Brossard,Arm,,,, +Julie Gaskin,Arm,,,, +Pranay Bakre,Arm,,,, +Elham Harirpoush,Arm,,,, +Frédéric -lefred- Descamps,OCI,,,,lefred.be +Kristof Beyls,Arm,,,, +David Spickett,Arm,,,, +Uma Ramalingam,Arm,uma-ramalingam,,, +Konstantinos Margaritis,VectorCamp,markos,konstantinosmargaritis,@freevec1,https://vectorcamp.gr/ +Diego Russo,Arm,diegorusso,diegor,diegor,https://www.diegor.it +Jonathan Davies,Arm,,,, +Zhengjun Xing,Arm,,,, +Diego Russo and Leandro Nunes,Arm,,,, +Dawid Borycki,,dawidborycki,,, +Ying Yu,Arm,,,, +Bolt Liu,Arm,,,, +Roberto Lopez Mendez,Arm,,,, +Arnaud de Grandmaison,Arm,Arnaud-de-Grandmaison-ARM,arnauddegrandmaison,, +Jose-Emilio Munoz-Lopez,Arm,,,, +James Whitaker,Arm,,,, +Johanna Skinnider,Arm,,,, +Varun Chari,Arm,,,, +Adnan AlSinan,Arm,,,, +Graham Woodward,Arm,,,, +Basma El Gaabouri,Arm,,,, +Gayathri Narayana Yegna Narayanan,Arm,,,, +Alexandros Lamprineas,Arm,,,, +Annie Tallund,Arm,annietllnd,annietallund,, +Cyril Rohr,RunsOn,crohr,cyrilrohr,, +Rin Dobrescu,Arm,,,, +Przemyslaw Wirkus,Arm,PrzemekWirkus,przemyslaw-wirkus-78b73352,, +Nader Zouaoui,Day Devs,nader-zouaoui,nader-zouaoui,@zouaoui_nader,https://daydevs.com/ +Alaaeddine Chakroun,Day Devs,Alaaeddine-Chakroun,alaaeddine-chakroun,,https://daydevs.com/ +Koki Mitsunami,Arm,,kmitsunami,, +Chen Zhang,Zilliz,,,, +Tianyu Li,Arm,,,, +Georgios Mermigkis,VectorCamp,gMerm,georgios-mermigkis,,https://vectorcamp.gr/ +Ben Clark,Arm,,,, +Han Yin,Arm,hanyin-arm,nacosiren,, +Willen Yang,Arm,,,, +Joe Stech,Arm,JoeStech,joestech,, +>>>>>>> Stashed changes diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md new file mode 100644 index 0000000000..eb65eece64 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md @@ -0,0 +1,21 @@ +--- +title: CDK installation +weight: 2 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Infrastructure as Code + +AWS CDK is an AWS-native Infrastructure as Code tool that allows cloud engineers to write IaC templates in many different languages. Regardless of the language used, all CDK code eventually transpiles to TypeScript, and the TypeScript generates CloudFormation templates, which then deploy the specified resources. + +This Learning Path will use the Python flavor of AWS CDK, because the Copilot Extension that will be deployed is also written in Python. Writing both IaC and application code in the same language is helpful for certain teams, especially those without dedicated platform engineers. + +## Installing AWS CDK + +To install the required packages, you will first need npm and Python installed. + + + + diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md new file mode 100644 index 0000000000..f884b5592b --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md @@ -0,0 +1,13 @@ +--- +title: PLACEHOLDER STEP TITLE 2 +weight: 3 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## PLACEHOLDER HEADER OF SECOND STEP +YOUR CONTENT GOES HERE + +IMAGE HERE: +![example image alt-text#center](example-picture.png "Figure 1. Example image caption") diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-cdk-deployment.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-cdk-deployment.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md new file mode 100644 index 0000000000..2c691e8e8a --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md @@ -0,0 +1,43 @@ +--- +title: Infrastructure Deployment for a GitHub Copilot Extension on Graviton + +draft: true +cascade: + draft: true + +minutes_to_complete: 20 + +who_is_this_for: This is an intermediate-level topic for software developers who want to learn how to deploy all necessary infrastructure on AWS for a GitHub Copilot Extension. + +learning_objectives: + - Understand the AWS services needed to host a GitHub Copilot Extension + - Create a CDK deployment for the required AWS services + - Add your newly generated endpoints to the GitHub app you previously created + +prerequisites: + - Information in the Build a GitHub Copilot Extension in Python Learning Path + - Basic knowledge of Infrastructure as Code + - A GitHub account + - A linux-based computer with npm and Python installed + +author_primary: Joe Stech + +### Tags +skilllevels: Intermediate +subjects: ML +armips: + - Neoverse +tools_software_languages: + - Python + - AWS CDK + - GitHub +operatingsystems: + - Linux + + +### FIXED, DO NOT MODIFY +# ================================================================================ +weight: 1 # _index.md always has weight of 1 to order correctly +layout: "learningpathall" # All files under learning paths have this same wrapper +learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. +--- diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_next-steps.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_next-steps.md new file mode 100644 index 0000000000..c4ae77cc92 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_next-steps.md @@ -0,0 +1,27 @@ +--- +next_step_guidance: PLACEHOLDER TEXT 1 + +recommended_path: /learning-paths/PLACEHOLDER_CATEGORY/PLACEHOLDER_LEARNING_PATH/ + +further_reading: + - resource: + title: PLACEHOLDER MANUAL + link: PLACEHOLDER MANUAL LINK + type: documentation + - resource: + title: PLACEHOLDER BLOG + link: PLACEHOLDER BLOG LINK + type: blog + - resource: + title: PLACEHOLDER GENERAL WEBSITE + link: PLACEHOLDER GENERAL WEBSITE LINK + type: website + + +# ================================================================================ +# FIXED, DO NOT MODIFY +# ================================================================================ +weight: 21 # set to always be larger than the content in this path, and one more than 'review' +title: "Next Steps" # Always the same +layout: "learningpathall" # All files under learning paths have this same wrapper +--- diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/example-picture.png b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/example-picture.png new file mode 100644 index 0000000000000000000000000000000000000000..c69844bed44b65c7f5bc6cf511f93987fdcd7b95 GIT binary patch literal 63167 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 q-Mv3pxc`&l*gyNt{xlEs;Qs>q{#983 literal 0 HcmV?d00001 From f2fba68addb853612944e221b4241eb5b6633aaf Mon Sep 17 00:00:00 2001 From: Joe <4088382+JoeStech@users.noreply.github.com> Date: Sat, 8 Feb 2025 22:37:27 -0700 Subject: [PATCH 02/15] change page stubs --- assets/contributors.csv | 59 +------------------ .../1-cdk-installation.md | 26 +++++++- .../2-cdk-services.md | 6 +- .../3-cdk-deployment.md | 12 ++++ .../4-github-config.md | 12 ++++ 5 files changed, 55 insertions(+), 60 deletions(-) diff --git a/assets/contributors.csv b/assets/contributors.csv index d6ddf26671..dc0553570d 100644 --- a/assets/contributors.csv +++ b/assets/contributors.csv @@ -1,4 +1,3 @@ -<<<<<<< Updated upstream author,company,github,linkedin,twitter,website Jason Andrews,Arm,jasonrandrews,jason-andrews-7b05a8,, Pareena Verma,Arm,pareenaverma,pareena-verma-7853607,, @@ -79,59 +78,5 @@ Masoud Koleini,,,,, Na Li,Arm,,,, Tom Pilar,,,,, Cyril Rohr,,,,, -Odin Shen,Arm,,,, -======= -author,company,github,linkedin,twitter,website -Jason Andrews,Arm,jasonrandrews,jason-andrews-7b05a8,, -Pareena Verma,Arm,pareenaverma,pareena-verma-7853607,, -Ronan Synnott,Arm,,ronansynnott,, -Florent Lebeau,Arm,,,, -Brenda Strech,Remote.It,bstrech,bstrech,@remote_it,www.remote.it -Liliya Wu,Arm,Liliyaw,liliya-wu-8b6227216,, -Julio Suarez,Arm,jsrz,juliosuarez,, -Gabriel Peterson,Arm,gabrieldpeterson,gabrieldpeterson,@gabedpeterson,https://corteximplant.com/@gabe -Christopher Seidl,Arm,,,, -Michael Hall,Arm,,,, -Kasper Mecklenburg,Arm,,,, -Mathias Brossard,Arm,,,, -Julie Gaskin,Arm,,,, -Pranay Bakre,Arm,,,, -Elham Harirpoush,Arm,,,, -Frédéric -lefred- Descamps,OCI,,,,lefred.be -Kristof Beyls,Arm,,,, -David Spickett,Arm,,,, -Uma Ramalingam,Arm,uma-ramalingam,,, -Konstantinos Margaritis,VectorCamp,markos,konstantinosmargaritis,@freevec1,https://vectorcamp.gr/ -Diego Russo,Arm,diegorusso,diegor,diegor,https://www.diegor.it -Jonathan Davies,Arm,,,, -Zhengjun Xing,Arm,,,, -Diego Russo and Leandro Nunes,Arm,,,, -Dawid Borycki,,dawidborycki,,, -Ying Yu,Arm,,,, -Bolt Liu,Arm,,,, -Roberto Lopez Mendez,Arm,,,, -Arnaud de Grandmaison,Arm,Arnaud-de-Grandmaison-ARM,arnauddegrandmaison,, -Jose-Emilio Munoz-Lopez,Arm,,,, -James Whitaker,Arm,,,, -Johanna Skinnider,Arm,,,, -Varun Chari,Arm,,,, -Adnan AlSinan,Arm,,,, -Graham Woodward,Arm,,,, -Basma El Gaabouri,Arm,,,, -Gayathri Narayana Yegna Narayanan,Arm,,,, -Alexandros Lamprineas,Arm,,,, -Annie Tallund,Arm,annietllnd,annietallund,, -Cyril Rohr,RunsOn,crohr,cyrilrohr,, -Rin Dobrescu,Arm,,,, -Przemyslaw Wirkus,Arm,PrzemekWirkus,przemyslaw-wirkus-78b73352,, -Nader Zouaoui,Day Devs,nader-zouaoui,nader-zouaoui,@zouaoui_nader,https://daydevs.com/ -Alaaeddine Chakroun,Day Devs,Alaaeddine-Chakroun,alaaeddine-chakroun,,https://daydevs.com/ -Koki Mitsunami,Arm,,kmitsunami,, -Chen Zhang,Zilliz,,,, -Tianyu Li,Arm,,,, -Georgios Mermigkis,VectorCamp,gMerm,georgios-mermigkis,,https://vectorcamp.gr/ -Ben Clark,Arm,,,, -Han Yin,Arm,hanyin-arm,nacosiren,, -Willen Yang,Arm,,,, -Joe Stech,Arm,JoeStech,joestech,, ->>>>>>> Stashed changes +Odin Shen,Arm,,,, +Joe Stech,Arm,JoeStech,joestech,, \ No newline at end of file diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md index eb65eece64..c3cc308db7 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md @@ -14,8 +14,32 @@ This Learning Path will use the Python flavor of AWS CDK, because the Copilot Ex ## Installing AWS CDK -To install the required packages, you will first need npm and Python installed. +To install the required packages, you will need npm and Python installed. Next, run +```bash +npm install -g aws-cdk +``` +To verify that the installation was successful, run +```bash +cdk --version +``` + +You should see a version number returned, signifying success. + +After the cdk cli is installed, you can use it to create a new python cdk environment: + +```bash +cdk init app --language python +``` + +This will set up convenient file stubs, as well as create a requirements.txt file with the Python CDK libraries required. Install these: + +```bash +source .venv/bin/activate +pip install -r requirements.txt +``` + +Now you are ready to specify the AWS services needed for your GitHub Copilot Extension. diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md index f884b5592b..4e72a57b90 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md @@ -1,12 +1,14 @@ --- -title: PLACEHOLDER STEP TITLE 2 +title: Deployment services weight: 3 ### FIXED, DO NOT MODIFY layout: learningpathall --- -## PLACEHOLDER HEADER OF SECOND STEP +In [the first GitHub Copilot Extension Learning Path]() you used + +## VPC YOUR CONTENT GOES HERE IMAGE HERE: diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-cdk-deployment.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-cdk-deployment.md index e69de29bb2..238d3c94cd 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-cdk-deployment.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-cdk-deployment.md @@ -0,0 +1,12 @@ +--- +title: Deployment services +weight: 4 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +In [the first GitHub Copilot Extension Learning Path]() you used + +## VPC +YOUR CONTENT GOES HERE \ No newline at end of file diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md index e69de29bb2..7d3cd37e58 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md @@ -0,0 +1,12 @@ +--- +title: Deployment services +weight: 5 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +In [the first GitHub Copilot Extension Learning Path]() you used + +## VPC +YOUR CONTENT GOES HERE \ No newline at end of file From 1754f58885da883b37c2b3927826b7a9a99a919a Mon Sep 17 00:00:00 2001 From: Joe <4088382+JoeStech@users.noreply.github.com> Date: Sun, 9 Feb 2025 14:13:45 -0700 Subject: [PATCH 03/15] add instructions to install AWS services via CDK --- .../1-cdk-installation.md | 8 +- .../2-cdk-services.md | 205 +++++++++++++++++- .../3-cdk-deployment.md | 12 - .../4-github-config.md | 12 - .../copilot-extension-deployment/_index.md | 25 ++- .../_next-steps.md | 27 +-- 6 files changed, 228 insertions(+), 61 deletions(-) delete mode 100644 content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-cdk-deployment.md delete mode 100644 content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md index c3cc308db7..f6d9f5c6e8 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md @@ -6,13 +6,13 @@ weight: 2 layout: learningpathall --- -## Infrastructure as Code +## What is AWS CDK? AWS CDK is an AWS-native Infrastructure as Code tool that allows cloud engineers to write IaC templates in many different languages. Regardless of the language used, all CDK code eventually transpiles to TypeScript, and the TypeScript generates CloudFormation templates, which then deploy the specified resources. This Learning Path will use the Python flavor of AWS CDK, because the Copilot Extension that will be deployed is also written in Python. Writing both IaC and application code in the same language is helpful for certain teams, especially those without dedicated platform engineers. -## Installing AWS CDK +## How do I install AWS CDK? To install the required packages, you will need npm and Python installed. Next, run @@ -31,6 +31,8 @@ You should see a version number returned, signifying success. After the cdk cli is installed, you can use it to create a new python cdk environment: ```bash +mkdir copilot-extension-deployment +cd copilot-extension-deployment cdk init app --language python ``` @@ -41,5 +43,7 @@ source .venv/bin/activate pip install -r requirements.txt ``` +For more information, see Amazon's [Working with the AWS CDK in Python](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-python.html) documentation. + Now you are ready to specify the AWS services needed for your GitHub Copilot Extension. diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md index 4e72a57b90..95263ec1c7 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md @@ -1,15 +1,208 @@ --- -title: Deployment services +title: Deploying AWS services weight: 3 ### FIXED, DO NOT MODIFY layout: learningpathall --- +## What AWS services do I need? -In [the first GitHub Copilot Extension Learning Path]() you used +In [the first GitHub Copilot Extension Learning Path](learning-paths/servers-and-cloud-computing/gh-copilot-simple) you ran a GitHub Copilot Extension from a single Linux computer, with the public URL being provided by an ngrok tunnel to your localhost. -## VPC -YOUR CONTENT GOES HERE +In an actual production environment, you'll want: + +* A domain that you own with DNS settings that you control (you can get this through AWS Route 53) +* A load balancer (AWS ALB) +* An auto-scaling cluster (AWS ASG) in a private virtual cloud subnet (AWS VPC) that you can adjust the size of based on load + +In order to use your custom domain with your ALB, you'll also need a custom TLS certificate in order to allow the ALB to do TLS termination before the ALB forwards the packets to your ASG instances. + +The following sections will walk you through setting up all these required services in AWS CDK. + +## Imports + +You will have an auto-generated folder called `copilot_extension_deployment` within the `copilot-extension-deployment` that you previously created. It will contain a file called `copilot_extension_deployment_stack.py`. Open this file, and add the following import lines: + +```python +from aws_cdk import ( + Stack, + aws_ec2 as ec2, + aws_elasticloadbalancingv2 as elbv2, + aws_autoscaling as autoscaling, + aws_iam as iam, + CfnOutput, + aws_certificatemanager as acm, + aws_route53 as route53, + aws_route53_targets as targets +) +``` + +Then, within the generated class, add the services denoted in the sections below. + +## Virtual Private Cloud (VPC) + +This will create a VPC with a public and private subnet. These subnets have a CIDR mask of 24, which means you'll get 256 total IPs in each subnet. If you need more than this, adjust accordingly. + +```python +vpc = ec2.Vpc(self, "FlaskStackVPC", + max_azs=2, + subnet_configuration=[ + ec2.SubnetConfiguration( + name="Private", + subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS, + cidr_mask=24 + ), + ec2.SubnetConfiguration( + name="Public", + subnet_type=ec2.SubnetType.PUBLIC, + cidr_mask=24 + ) + ] + ) +``` + +You'll also need a security group for the EC2 instances, along with egress on port 5000: + +```python +security_group = ec2.SecurityGroup(self, "EC2SecurityGroup", + vpc=vpc, + allow_all_outbound=True, + description="Security group for EC2 instances" + ) +security_group.add_ingress_rule(ec2.Peer.any_ipv4(), ec2.Port.tcp(5000), "Allow incoming traffic on port 5000") +``` + +## EC2 + +Once you have your VPC templates set up, you can use them in your EC2 templates. + +First, create a User Data script for all the EC2 templates that will launch in your auto-scaling group. This will install an SSM agent and the AWS CLI, for later convenience: + +```python +user_data = ec2.UserData.for_linux() +user_data.add_commands( + "apt-get update", + # Install SSM agent + "sudo snap install amazon-ssm-agent --classic", + "sudo systemctl enable snap.amazon-ssm-agent.amazon-ssm-agent.service", + "sudo systemctl start snap.amazon-ssm-agent.amazon-ssm-agent.service", + # Install AWS CLI v2 + "apt install unzip", + 'curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip"', + "unzip awscliv2.zip", + "sudo ./aws/install", + # add any additional commands that you'd like to run on instance launch here +) +``` + +After the launch template, you'll want to get the latest Ubuntu 24.04 Arm AMI: + +```python +ubuntu_arm_ami = ec2.MachineImage.lookup( + name="ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-arm64-server-*", + owners=["099720109477"], # Canonical's AWS account ID + filters={"architecture": ["arm64"]} +) +``` + +Next create an IAM role that will allow your EC2 instances to use the SSM agent, write logs to CloudWatch, and access AWS S3: + +```Python +ec2_role_name = "Proj-Flask-LLM-ALB-EC2-Role" +ec2_role = iam.Role(self, "EC2Role", + assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"), + managed_policies=[ + iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore"), + iam.ManagedPolicy.from_aws_managed_policy_name("CloudWatchAgentServerPolicy"), + iam.ManagedPolicy.from_aws_managed_policy_name("CloudWatchLogsFullAccess"), + iam.ManagedPolicy.from_aws_managed_policy_name("AmazonS3FullAccess") + ], + role_name=ec2_role_name, + ) +``` + +Now pull all these elements together in the launch template that the ASG will use: + +```Python +launch_template = ec2.LaunchTemplate(self, "LaunchTemplate", + instance_type=ec2.InstanceType("c8g.xlarge"), + machine_image=ubuntu_arm_ami, + user_data=user_data, + security_group=security_group, + role=ec2_role, + detailed_monitoring=True, + block_devices=[ + ec2.BlockDevice( + device_name="/dev/sda1", + volume=ec2.BlockDeviceVolume.ebs( + volume_size=50, + volume_type=ec2.EbsDeviceVolumeType.GP3, + delete_on_termination=True + ) + ) + ] + ) +``` + +Finally, create the ASG, specifying the launch template you just created as the launch template for the EC2 instances within the ASG: + +```Python +asg = autoscaling.AutoScalingGroup(self, "ASG", + vpc=vpc, + vpc_subnets=ec2.SubnetSelection( + subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS), + launch_template=launch_template, + min_capacity=1, + max_capacity=1, + desired_capacity=1 + ) +``` + +As you can see, you'll want the instances inside your private subnet for security, and you only need one instance to begin with. You can scale manually later on, or create an autoscaling function, depending on your needs. + +## Application Load Balancer (ALB) + +First, create an ALB using the VPC resources you previously specified, within the PUBLIC subnet: + +```Python +alb = elbv2.ApplicationLoadBalancer(self, "ALB", + vpc=vpc, + internet_facing=True, + vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PUBLIC) + ) +``` + +Next add a custom certificate. You'll need to generate this certificate beforehand. If you want to do this from the AWS console, see [Getting Started with AWS Certificate Manager](https://aws.amazon.com/certificate-manager/getting-started/). + +Replace `ACM_CERTIFICATE_ARN` with the ARN of your newly created certificate: + +```Python +certificate = acm.Certificate.from_certificate_arn( + self, + "Certificate", + os.environ["ACM_CERTIFICATE_ARN"] +) +``` + +Next configure a listener for the ALB that uses the certificate and adds the ASG as a target, listening on port 8080 (this is where you'll serve your Flask app): + +```Python +# Add a listener to the ALB with HTTPS +listener = alb.add_listener("HttpsListener", + port=443, + certificates=[certificate], + ssl_policy=elbv2.SslPolicy.RECOMMENDED) + +# Add the ASG as a target to the ALB listener +listener.add_targets("ASGTarget", + port=8080, + targets=[asg], + protocol=elbv2.ApplicationProtocol.HTTP, + health_check=elbv2.HealthCheck( + path="/health", + healthy_http_codes="200-299" + )) +``` + +## Route 53 -IMAGE HERE: -![example image alt-text#center](example-picture.png "Figure 1. Example image caption") diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-cdk-deployment.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-cdk-deployment.md deleted file mode 100644 index 238d3c94cd..0000000000 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-cdk-deployment.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Deployment services -weight: 4 - -### FIXED, DO NOT MODIFY -layout: learningpathall ---- - -In [the first GitHub Copilot Extension Learning Path]() you used - -## VPC -YOUR CONTENT GOES HERE \ No newline at end of file diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md deleted file mode 100644 index 7d3cd37e58..0000000000 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Deployment services -weight: 5 - -### FIXED, DO NOT MODIFY -layout: learningpathall ---- - -In [the first GitHub Copilot Extension Learning Path]() you used - -## VPC -YOUR CONTENT GOES HERE \ No newline at end of file diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md index 2c691e8e8a..47e8f7bf72 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md @@ -1,9 +1,5 @@ --- -title: Infrastructure Deployment for a GitHub Copilot Extension on Graviton - -draft: true -cascade: - draft: true +title: Graviton Infrastructure for GitHub Copilot Extensions minutes_to_complete: 20 @@ -20,7 +16,7 @@ prerequisites: - A GitHub account - A linux-based computer with npm and Python installed -author_primary: Joe Stech +author: Joe Stech ### Tags skilllevels: Intermediate @@ -35,6 +31,23 @@ operatingsystems: - Linux + +further_reading: + - resource: + title: PLACEHOLDER MANUAL + link: PLACEHOLDER MANUAL LINK + type: documentation + - resource: + title: PLACEHOLDER BLOG + link: PLACEHOLDER BLOG LINK + type: blog + - resource: + title: PLACEHOLDER GENERAL WEBSITE + link: PLACEHOLDER GENERAL WEBSITE LINK + type: website + + + ### FIXED, DO NOT MODIFY # ================================================================================ weight: 1 # _index.md always has weight of 1 to order correctly diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_next-steps.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_next-steps.md index c4ae77cc92..c3db0de5a2 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_next-steps.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_next-steps.md @@ -1,27 +1,8 @@ --- -next_step_guidance: PLACEHOLDER TEXT 1 - -recommended_path: /learning-paths/PLACEHOLDER_CATEGORY/PLACEHOLDER_LEARNING_PATH/ - -further_reading: - - resource: - title: PLACEHOLDER MANUAL - link: PLACEHOLDER MANUAL LINK - type: documentation - - resource: - title: PLACEHOLDER BLOG - link: PLACEHOLDER BLOG LINK - type: blog - - resource: - title: PLACEHOLDER GENERAL WEBSITE - link: PLACEHOLDER GENERAL WEBSITE LINK - type: website - - # ================================================================================ -# FIXED, DO NOT MODIFY +# FIXED, DO NOT MODIFY THIS FILE # ================================================================================ -weight: 21 # set to always be larger than the content in this path, and one more than 'review' -title: "Next Steps" # Always the same -layout: "learningpathall" # All files under learning paths have this same wrapper +weight: 21 # Set to always be larger than the content in this path to be at the end of the navigation. +title: "Next Steps" # Always the same, html page title. +layout: "learningpathall" # All files under learning paths have this same wrapper for Hugo processing. --- From 3f544446f469d6160022c302b57ecf01985431a8 Mon Sep 17 00:00:00 2001 From: Joe <4088382+JoeStech@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:39:24 -0700 Subject: [PATCH 04/15] deployment piece --- .../2-cdk-services.md | 26 +++++++++++++++++++ .../copilot-extension-deployment/_index.md | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md index 95263ec1c7..5bf98eff20 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md @@ -206,3 +206,29 @@ listener.add_targets("ASGTarget", ## Route 53 +The final step in setting up your AWS services is to add an ALB-linked A record to the hosted zone for your domain. This makes sure that when GitHub invokes your API, the DNS is pointed to the IP of your ALB. You will need to replace `HOSTED_ZONE_DOMAIN_NAME` with your hosted zone domain, and replace `SUBDOMAIN_NAME` with the subdomain that maps to the ACM certificate that you generated and used in your ALB. + +```Python +hosted_zone = route53.HostedZone.from_lookup(self, "HostedZone", + domain_name=os.environ["HOSTED_ZONE_DOMAIN_NAME"], + ) + +# Create an A record for the subdomain +route53.ARecord(self, "ALBDnsRecord", + zone=hosted_zone, + record_name=os.environ["SUBDOMAIN_NAME"], + target=route53.RecordTarget.from_alias(targets.LoadBalancerTarget(alb)) + ) +``` + +## How do I deploy? + +Once you have added all of the sections above to your `copilot_extension_deployment_stack.py` file, you can deploy your services to AWS. You must first ensure that your CDK environment in AWS is 'bootstrapped', which means that the AWS CDK has created all the resources it needs to use when deploying (IAM roles, an ECR repo for images, and buckets for artifacts). The bootstrap process is a one-time deal, but if you haven't yet bootstrapped, please see the AWS guide "[Bootstrap your environment for use with the AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping-env.html)". In general it's as easy as running `cdk bootstrap`, but if your organization has governance rules in place regarding naming conventions you'll need a custom bootstrap yaml. + +Once your environment has been bootstrapped, you can run + +```bash +cdk deploy +``` + +from within the directory that includes your stack file. This deployment will take a few minutes, as CloudFormation deploys your resources. \ No newline at end of file diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md index 47e8f7bf72..d93193eb93 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md @@ -14,7 +14,7 @@ prerequisites: - Information in the Build a GitHub Copilot Extension in Python Learning Path - Basic knowledge of Infrastructure as Code - A GitHub account - - A linux-based computer with npm and Python installed + - A linux-based computer with npm, Python, and the AWS CLI installed author: Joe Stech From cd3177c75af74ec58551290017c8fb51642f6108 Mon Sep 17 00:00:00 2001 From: Joe <4088382+JoeStech@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:39:56 -0700 Subject: [PATCH 05/15] new files --- .../3-flask-deployment.md | 68 +++++++++++++++++++ .../4-github-config.md | 12 ++++ 2 files changed, 80 insertions(+) create mode 100644 content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-flask-deployment.md create mode 100644 content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-flask-deployment.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-flask-deployment.md new file mode 100644 index 0000000000..55a58617fe --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-flask-deployment.md @@ -0,0 +1,68 @@ +--- +title: Deploying Flask +weight: 4 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## How do I deploy my Copilot Extension Flask app to my newly created EC2 instance? + +In the first GitHub Copilot Extension Learning Path you created a Flask app in the section titled "[How can I create my own private GitHub Copilot Extension?](http://localhost:1313/learning-paths/servers-and-cloud-computing/gh-copilot-simple/run-python/)". + +You will deploy this Flask app on your newly created EC2 instance. First, get your EC2 instance ID: + +```bash +aws ec2 describe-instances --filters "Name=tag:Name,Values=CopilotExtensionDeploymentStack/LaunchTemplate" --query "Reservations[*].Instances[*].InstanceId" --output text +``` + +Then use that ID to log in with AWS SSM. You must use AWS SSM because your instance is in a private subnet for security purposes, but because the SSM agent is running on the instance, it creates a tunnel that allows you to SSH into the machine with the following command: + +```bash +aws ssm start-session --target [your instance ID] +``` + +You should now be able to go through the steps in "[How can I create my own private GitHub Copilot Extension?](http://localhost:1313/learning-paths/servers-and-cloud-computing/gh-copilot-simple/run-python/)" to create your Flask app, create a Python virtual environment, and install the appropriate packages. + +The only two changes you'll make are to add a health check endpoint (for the ALB health check), and to run your app on 0.0.0.0 port 8080, which the ALB is listening for. + +First, add the following endpoint: + +```Python +@app.route('/health') +def health(): + return Response(status=200) +``` + +Next, replace the final lines of your flask app. Find: + +```Python +port = int(os.environ.get("PORT", 3000)) +if __name__ == "__main__": + app.run(port=port) +``` + +and replace with: + +```Python +if __name__ == '__main__': + app.run(host='0.0.0.0', port=8080) +``` + +This will expose your app to the port that you set up your ALB listener to listen on. + +Now, when you run: + +```Python +python ./simple-extension.py +``` + +You should now be able to navigate to your API subdomain from any browser and see + +```text +"Hello! Welcome to the example GitHub Copilot Extension in Python!" +``` + + +## VPC +YOUR CONTENT GOES HERE \ No newline at end of file diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md new file mode 100644 index 0000000000..7d3cd37e58 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md @@ -0,0 +1,12 @@ +--- +title: Deployment services +weight: 5 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +In [the first GitHub Copilot Extension Learning Path]() you used + +## VPC +YOUR CONTENT GOES HERE \ No newline at end of file From 9a9106475389263e981632cf4331499ec61d4b5d Mon Sep 17 00:00:00 2001 From: Joe <4088382+JoeStech@users.noreply.github.com> Date: Thu, 13 Feb 2025 17:02:28 -0700 Subject: [PATCH 06/15] config changes --- .../3-flask-deployment.md | 4 +--- .../4-github-config.md | 21 +++++++++++++---- .../copilot-extension-deployment/_index.md | 22 +++++++----------- .../configure.png | Bin 0 -> 28381 bytes 4 files changed, 27 insertions(+), 20 deletions(-) create mode 100644 content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/configure.png diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-flask-deployment.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-flask-deployment.md index 55a58617fe..2144f104b1 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-flask-deployment.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-flask-deployment.md @@ -63,6 +63,4 @@ You should now be able to navigate to your API subdomain from any browser and se "Hello! Welcome to the example GitHub Copilot Extension in Python!" ``` - -## VPC -YOUR CONTENT GOES HERE \ No newline at end of file +Your API is now complete and ready to be configured in your GitHub Application. \ No newline at end of file diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md index 7d3cd37e58..4adbe340f6 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/4-github-config.md @@ -1,12 +1,25 @@ --- -title: Deployment services +title: Configuring GitHub weight: 5 ### FIXED, DO NOT MODIFY layout: learningpathall --- -In [the first GitHub Copilot Extension Learning Path]() you used +## How do I configure my GitHub Application to use my API? -## VPC -YOUR CONTENT GOES HERE \ No newline at end of file +Open the GitHub App that you created in [the first GitHub Copilot Extension Learning Path](learning-paths/servers-and-cloud-computing/gh-copilot-simple). + +Navigate to the 'Copilot' tab, and add your URL to the field under the 'Agent Definition' section: + + ![Configure URL](configure.png) + +You will also want to change the 'Callback URL' under the General tab. This is the full URL to redirect to after a user authorizes an installation. + +## Test your Extension + +You are now ready to test your productionized Extension. For guidance on testing, see [Test your Copilot Extension](http://localhost:1313/learning-paths/servers-and-cloud-computing/gh-copilot-simple/copilot-test/) in the previous Copilot Extension Learning Path. + +## Next Steps + +You are now ready to build a more advanced Copilot Extension that uses RAG techniques in [Create a RAG-based GitHub Copilot Extension in Python](../copilot-extension). \ No newline at end of file diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md index d93193eb93..d67b2f5dcc 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md @@ -3,7 +3,7 @@ title: Graviton Infrastructure for GitHub Copilot Extensions minutes_to_complete: 20 -who_is_this_for: This is an intermediate-level topic for software developers who want to learn how to deploy all necessary infrastructure on AWS for a GitHub Copilot Extension. +who_is_this_for: This is an advanced topic for software developers who want to learn how to deploy all necessary infrastructure on AWS for a GitHub Copilot Extension. learning_objectives: - Understand the AWS services needed to host a GitHub Copilot Extension @@ -11,15 +11,15 @@ learning_objectives: - Add your newly generated endpoints to the GitHub app you previously created prerequisites: - - Information in the Build a GitHub Copilot Extension in Python Learning Path - - Basic knowledge of Infrastructure as Code + - The "[Build a GitHub Copilot Extension in Python](../gh-copilot-simple/)" Learning Path. + - Understanding of Infrastructure as Code - A GitHub account - A linux-based computer with npm, Python, and the AWS CLI installed author: Joe Stech ### Tags -skilllevels: Intermediate +skilllevels: Advanced subjects: ML armips: - Neoverse @@ -34,17 +34,13 @@ operatingsystems: further_reading: - resource: - title: PLACEHOLDER MANUAL - link: PLACEHOLDER MANUAL LINK + title: About building Copilot Extensions + link: https://docs.github.com/en/copilot/building-copilot-extensions/about-building-copilot-extensions/ type: documentation - resource: - title: PLACEHOLDER BLOG - link: PLACEHOLDER BLOG LINK - type: blog - - resource: - title: PLACEHOLDER GENERAL WEBSITE - link: PLACEHOLDER GENERAL WEBSITE LINK - type: website + title: Copilot Extensions repository + link: https://github.com/copilot-extensions/ + type: documentation diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/configure.png b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/configure.png new file mode 100644 index 0000000000000000000000000000000000000000..1ace61c92335043c8f147e299b2e17e7d170df8b GIT binary patch literal 28381 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

*;MboO|9s75y4$Zd0OnhoSy=NRn)Va|m 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 Date: Thu, 13 Feb 2025 17:08:07 -0700 Subject: [PATCH 07/15] resolve merge conflict --- assets/contributors.csv | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/assets/contributors.csv b/assets/contributors.csv index ec74e55202..ed615296d0 100644 --- a/assets/contributors.csv +++ b/assets/contributors.csv @@ -63,7 +63,7 @@ Albin Bernhardsson,,,,, Przemyslaw Wirkus,,,,, Zach Lasiuk,,,,, Daniel Nguyen,,,,, -Joe Stech,Arm,,,, +Joe Stech,Arm,JoeStech,joestech,, visualSilicon,,,,, Konstantinos Margaritis,VectorCamp,,,, Kieran Hejmadi,,,,, @@ -78,9 +78,4 @@ Masoud Koleini,,,,, Na Li,Arm,,,, Tom Pilar,,,,, Cyril Rohr,,,,, -<<<<<<< HEAD -Odin Shen,Arm,,,, -Joe Stech,Arm,JoeStech,joestech,, -======= Odin Shen,Arm,odincodeshen,odin-shen-lmshen,, ->>>>>>> upstream/main From f25c31e172af2c9eec8e10c8977837e46a9f7d24 Mon Sep 17 00:00:00 2001 From: Joe <4088382+JoeStech@users.noreply.github.com> Date: Wed, 19 Feb 2025 17:03:11 -0700 Subject: [PATCH 08/15] address PR comments --- .../1-cdk-installation.md | 6 ++--- .../2-cdk-services.md | 23 +++++++++++++------ .../3-flask-deployment.md | 14 +++-------- .../copilot-extension-deployment/_index.md | 6 ++--- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md index f6d9f5c6e8..83d15c30a5 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/1-cdk-installation.md @@ -28,7 +28,7 @@ cdk --version You should see a version number returned, signifying success. -After the cdk cli is installed, you can use it to create a new python cdk environment: +After the CDK CLI is installed, you can use it to create a new Python CDK environment: ```bash mkdir copilot-extension-deployment @@ -36,14 +36,12 @@ cd copilot-extension-deployment cdk init app --language python ``` -This will set up convenient file stubs, as well as create a requirements.txt file with the Python CDK libraries required. Install these: +This will set up convenient file stubs, as well as create a `requirements.txt` file with the Python CDK libraries required. The `init` command uses the name of the project folder to name various elements of the project. Hyphens in the folder name are converted to underscores. Install the packages in the `requirements.txt`: ```bash source .venv/bin/activate pip install -r requirements.txt ``` -For more information, see Amazon's [Working with the AWS CDK in Python](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-python.html) documentation. - Now you are ready to specify the AWS services needed for your GitHub Copilot Extension. diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md index 5bf98eff20..3d689cee57 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/2-cdk-services.md @@ -37,11 +37,11 @@ from aws_cdk import ( ) ``` -Then, within the generated class, add the services denoted in the sections below. +Then, within the generated class (`class CopilotExtensionDeploymentStack(Stack):`) in the same file, add all the AWS services needed for your Extension deployment as described in the following sections. ## Virtual Private Cloud (VPC) -This will create a VPC with a public and private subnet. These subnets have a CIDR mask of 24, which means you'll get 256 total IPs in each subnet. If you need more than this, adjust accordingly. +The code below will create a VPC with a public and private subnet. These subnets have a CIDR mask of 24, which means you'll get 256 total IPs in each subnet. If you need more than this, adjust accordingly. ```python vpc = ec2.Vpc(self, "FlaskStackVPC", @@ -61,7 +61,7 @@ vpc = ec2.Vpc(self, "FlaskStackVPC", ) ``` -You'll also need a security group for the EC2 instances, along with egress on port 5000: +You'll also need a security group for the EC2 instances: ```python security_group = ec2.SecurityGroup(self, "EC2SecurityGroup", @@ -69,7 +69,6 @@ security_group = ec2.SecurityGroup(self, "EC2SecurityGroup", allow_all_outbound=True, description="Security group for EC2 instances" ) -security_group.add_ingress_rule(ec2.Peer.any_ipv4(), ec2.Port.tcp(5000), "Allow incoming traffic on port 5000") ``` ## EC2 @@ -204,7 +203,7 @@ listener.add_targets("ASGTarget", )) ``` -## Route 53 +## Custom domain setup in Route 53 The final step in setting up your AWS services is to add an ALB-linked A record to the hosted zone for your domain. This makes sure that when GitHub invokes your API, the DNS is pointed to the IP of your ALB. You will need to replace `HOSTED_ZONE_DOMAIN_NAME` with your hosted zone domain, and replace `SUBDOMAIN_NAME` with the subdomain that maps to the ACM certificate that you generated and used in your ALB. @@ -223,9 +222,19 @@ route53.ARecord(self, "ALBDnsRecord", ## How do I deploy? -Once you have added all of the sections above to your `copilot_extension_deployment_stack.py` file, you can deploy your services to AWS. You must first ensure that your CDK environment in AWS is 'bootstrapped', which means that the AWS CDK has created all the resources it needs to use when deploying (IAM roles, an ECR repo for images, and buckets for artifacts). The bootstrap process is a one-time deal, but if you haven't yet bootstrapped, please see the AWS guide "[Bootstrap your environment for use with the AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping-env.html)". In general it's as easy as running `cdk bootstrap`, but if your organization has governance rules in place regarding naming conventions you'll need a custom bootstrap yaml. +Once you have added all of the sections above to your `copilot_extension_deployment_stack.py` file, you can deploy your services to AWS. You must first ensure that your CDK environment in AWS is 'bootstrapped', which means that the AWS CDK has created all the resources it needs to use when deploying (IAM roles, an ECR repo for images, and buckets for artifacts). The bootstrap process is a one-time deal, and can generally be done by running: -Once your environment has been bootstrapped, you can run +```bash +cdk bootstrap aws://123456789012/us-east-1 +``` + +Replace the AWS account and region with your account and region. + +{{% notice Note %}} +if your organization has governance rules in place regarding naming conventions you'll need a custom bootstrap yaml. To learn more about custom bootstrapping, see the [AWS guide on Bootstrapping your environment for use with the AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping-env.html). +{{% /notice %}} + +Once your environment has been bootstrapped, you can run: ```bash cdk deploy diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-flask-deployment.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-flask-deployment.md index 2144f104b1..6dfd1814b8 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-flask-deployment.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/3-flask-deployment.md @@ -26,7 +26,7 @@ You should now be able to go through the steps in "[How can I create my own priv The only two changes you'll make are to add a health check endpoint (for the ALB health check), and to run your app on 0.0.0.0 port 8080, which the ALB is listening for. -First, add the following endpoint: +First, add the following endpoint to your main flask file: ```Python @app.route('/health') @@ -34,15 +34,7 @@ def health(): return Response(status=200) ``` -Next, replace the final lines of your flask app. Find: - -```Python -port = int(os.environ.get("PORT", 3000)) -if __name__ == "__main__": - app.run(port=port) -``` - -and replace with: +Next, add the `host` argument to the `app.run` call at the end of the file and update the port number. The final result should look like this: ```Python if __name__ == '__main__': @@ -51,7 +43,7 @@ if __name__ == '__main__': This will expose your app to the port that you set up your ALB listener to listen on. -Now, when you run: +Run the simple extension: ```Python python ./simple-extension.py diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md index d67b2f5dcc..393285376c 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md @@ -7,12 +7,12 @@ who_is_this_for: This is an advanced topic for software developers who want to l learning_objectives: - Understand the AWS services needed to host a GitHub Copilot Extension - - Create a CDK deployment for the required AWS services + - Create an AWS CDK (Cloud Development Kit) deployment for the required AWS services - Add your newly generated endpoints to the GitHub app you previously created prerequisites: - - The "[Build a GitHub Copilot Extension in Python](../gh-copilot-simple/)" Learning Path. - - Understanding of Infrastructure as Code + - The [Build a GitHub Copilot Extension in Python](../gh-copilot-simple/) Learning Path. + - Understanding of IoC (Infrastructure as Code) - A GitHub account - A linux-based computer with npm, Python, and the AWS CLI installed From e815a3938f765d6dfbae22872798ccdbfdd18fdd Mon Sep 17 00:00:00 2001 From: Arnaud de Grandmaison Date: Thu, 20 Feb 2025 15:50:51 +0100 Subject: [PATCH 09/15] [SME2] Replicated the directory change to the gitlab code example repository. Last change. --- .../multiplying-matrices-with-sme2/1-get-started.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/content/learning-paths/cross-platform/multiplying-matrices-with-sme2/1-get-started.md b/content/learning-paths/cross-platform/multiplying-matrices-with-sme2/1-get-started.md index 72a84db8e6..932afa453c 100644 --- a/content/learning-paths/cross-platform/multiplying-matrices-with-sme2/1-get-started.md +++ b/content/learning-paths/cross-platform/multiplying-matrices-with-sme2/1-get-started.md @@ -103,19 +103,19 @@ For more examples and ideas, visit: ## Environment -Now, [download the code examples](https://gitlab.arm.com/learning-code-examples/code-examples/-/archive/main/code-examples-main.tar.gz?path=learning-paths/cross-platform/sme2) +Now, [download the code examples](https://gitlab.arm.com/learning-code-examples/code-examples/-/archive/main/code-examples-main.tar.gz?path=learning-paths/cross-platform/multiplying-matrices-with-sme2) for this learning path, expand the archive and change your current directory to ``code-examples/learning-paths/cross-platform/sme2`` : ```BASH -tar xfz code-examples-main-learning-paths-cross-platform-sme2.tar.gz -s /code-examples-main-learning-paths-cross-platform-sme2/code-examples/ -cd code-examples/learning-paths/cross-platform/sme2 +tar xfz code-examples-main-learning-paths-cross-platform-multiplying-matrices-with-sme2.tar.gz -s /code-examples-main-learning-paths-cross-platform-multiplying-matrices-with-sme2/code-examples/ +cd code-examples/learning-paths/cross-platform/multiplying-matrices-with-sme2 ``` This list of content in this directory should look like this : ```TXT -code-examples/learning-paths/cross-platform/sme2/ +code-examples/learning-paths/cross-platform/multiplying-matrices-with-sme2/ ├── .clang-format ├── .devcontainer/ │ └── devcontainer.json @@ -157,7 +157,7 @@ It contains: {{% notice Note %}} From this point in the Learning Path, all instructions assume that your current -directory is ``code-examples/learning-paths/cross-platform/sme2``.{{% /notice %}} +directory is ``code-examples/learning-paths/cross-platform/multiplying-matrices-with-sme2``.{{% /notice %}} ## Using the environment @@ -180,7 +180,7 @@ docker run --rm -v "$PWD:/work" -w /work armswdev/sme2-learning-path:sme2-enviro This invokes Docker, using the ``armswdev/sme2-learning-path:sme2-environment-v1``container -image, and mounts the current working directory (the ``code-examples.git/learning-paths/cross-platform/sme2``) +image, and mounts the current working directory (the ``code-examples/learning-paths/cross-platform/multiplying-matrices-with-sme2``) inside the container to ``/work``, then sets ``/work`` as the working directory and runs ``COMMAND ARGUMENTS`` in this environment. From 7a22d1b48eeb40d06b87ebc7279da55ace9d3965 Mon Sep 17 00:00:00 2001 From: chloebiscoearm Date: Thu, 20 Feb 2025 15:42:28 +0000 Subject: [PATCH 10/15] Update events.csv --- assets/events.csv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/events.csv b/assets/events.csv index eab519fcd4..e8cce6f32d 100644 --- a/assets/events.csv +++ b/assets/events.csv @@ -4,7 +4,7 @@ FOSDEM 25,2,2025-02-01,2025-02-02,Brussels,Belgium,FALSE,Example description. Ex State of Open,2,2025-02-04,2025-02-05,London,United Kingdom,FALSE,Example description. Example description. Example description. Example description. Example description.,https://stateofopencon.com/,Servers and Cloud Computing; AI; IoT Arm AI Innovation Day,2,2025-02-15,2025-02-15,Nairobi,Kenya,,Example description. Example description. Example description. Example description. Example description.,TBC,AI Rust Nation,2,2025-02-19,2025-02-20,London,United Kingdom,FALSE,Example description. Example description. Example description. Example description. Example description.,https://www.rustnationuk.com/,Embedded and Microcontrollers; AI; IoT -SOSS Policy Summit,2,2025-03-04,2025-03-04,Washington,United States,FALSE,Example description. Example description. Example description. Example description. Example description.,https://events.linuxfoundation.org/openssf-policy-summit-dc/,Servers and Cloud Computing +SOSS Policy Summit,2,2025-03-04,2025-03-04,Washington,United States,FALSE,Hosted by the Open Source Security Foundation (OpenSSF), an initiative of the Linux Foundation, this event addresses the security challenges associated with Open Source Software (OSS).,https://events.linuxfoundation.org/openssf-policy-summit-dc/,Servers and Cloud Computing SCaLE,1,2025-03-06,2025-03-09,Pasadena,United States,FALSE,SCaLE is the largest community-run open-source and free software conference in North America. It is held annually in the greater Los Angeles area.,https://www.socallinuxexpo.org/scale/22x,Servers and Cloud Computing SUSECon,2,2025-03-10,2025-03-14,Orlando,United States,FALSE,Example description. Example description. Example description. Example description. Example description.,https://www.suse.com/susecon/,Servers and Cloud Computing; AI; IoT Embedded World,1,2025-03-11,2025-03-13,Nuremburg,Germany,FALSE,"Embedded World offers insight into the world of embedded systems, from components and modules to operating systems, hardware and software design, M2M communication, and more.",https://www.embedded-world.de/en,Embedded and Microcontrollers; Automotive @@ -12,4 +12,4 @@ FOSSAsia,2,2025-03-13,2025-03-15,Bangkok,Thailand,TRUE,Example description. Exam NVIDIA GTC,1,2025-03-17,2025-03-21,San Jose,United States,TRUE,"Nvidia GTC is a global artificial intelligence conference for developers that brings together developers, engineers, researchers, inventors, and IT professionals. ",https://www.nvidia.com/gtc/,ML GDC,1,2025-03-17,2025-03-21,San Fransisco,United States,FALSE,"The Game Developers Conference (GDC) is the world's premier event for developers who make the games we love. GDC is the destination for creativity, innovation, and excellence.",https://gdconf.com/,"Mobile, Graphics, and Gaming" ATO AI,2,2025-03-17,2025-03-18,Durham,United States,,Example description. Example description. Example description. Example description. Example description.,https://allthingsopen.ai/,AI -KubeCon EU,2,2025-04-01,2025-04-04,London ,United Kingdom,TRUE,Example description. Example description. Example description. Example description. Example description.,https://events.linuxfoundation.org/kubecon-cloudnativecon-europe/,Servers and Cloud Computing \ No newline at end of file +KubeCon EU,2,2025-04-01,2025-04-04,London ,United Kingdom,TRUE,Example description. Example description. Example description. Example description. Example description.,https://events.linuxfoundation.org/kubecon-cloudnativecon-europe/,Servers and Cloud Computing From d76fab031b483c0fd6a93a9c3390030439f68a44 Mon Sep 17 00:00:00 2001 From: chloebiscoearm Date: Thu, 20 Feb 2025 15:43:40 +0000 Subject: [PATCH 11/15] Update events.csv --- assets/events.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/events.csv b/assets/events.csv index e8cce6f32d..6bbe492d56 100644 --- a/assets/events.csv +++ b/assets/events.csv @@ -4,7 +4,7 @@ FOSDEM 25,2,2025-02-01,2025-02-02,Brussels,Belgium,FALSE,Example description. Ex State of Open,2,2025-02-04,2025-02-05,London,United Kingdom,FALSE,Example description. Example description. Example description. Example description. Example description.,https://stateofopencon.com/,Servers and Cloud Computing; AI; IoT Arm AI Innovation Day,2,2025-02-15,2025-02-15,Nairobi,Kenya,,Example description. Example description. Example description. Example description. Example description.,TBC,AI Rust Nation,2,2025-02-19,2025-02-20,London,United Kingdom,FALSE,Example description. Example description. Example description. Example description. Example description.,https://www.rustnationuk.com/,Embedded and Microcontrollers; AI; IoT -SOSS Policy Summit,2,2025-03-04,2025-03-04,Washington,United States,FALSE,Hosted by the Open Source Security Foundation (OpenSSF), an initiative of the Linux Foundation, this event addresses the security challenges associated with Open Source Software (OSS).,https://events.linuxfoundation.org/openssf-policy-summit-dc/,Servers and Cloud Computing +SOSS Policy Summit,2,2025-03-04,2025-03-04,Washington,United States,FALSE,"Hosted by the Open Source Security Foundation (OpenSSF), an initiative of the Linux Foundation, this event addresses the security challenges associated with Open Source Software (OSS).",https://events.linuxfoundation.org/openssf-policy-summit-dc/,Servers and Cloud Computing SCaLE,1,2025-03-06,2025-03-09,Pasadena,United States,FALSE,SCaLE is the largest community-run open-source and free software conference in North America. It is held annually in the greater Los Angeles area.,https://www.socallinuxexpo.org/scale/22x,Servers and Cloud Computing SUSECon,2,2025-03-10,2025-03-14,Orlando,United States,FALSE,Example description. Example description. Example description. Example description. Example description.,https://www.suse.com/susecon/,Servers and Cloud Computing; AI; IoT Embedded World,1,2025-03-11,2025-03-13,Nuremburg,Germany,FALSE,"Embedded World offers insight into the world of embedded systems, from components and modules to operating systems, hardware and software design, M2M communication, and more.",https://www.embedded-world.de/en,Embedded and Microcontrollers; Automotive From 4fb994b52ac8ec5d3cce6cd3aeeb122f24f56da5 Mon Sep 17 00:00:00 2001 From: chloebiscoearm Date: Thu, 20 Feb 2025 15:55:30 +0000 Subject: [PATCH 12/15] Update events.csv --- assets/events.csv | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/assets/events.csv b/assets/events.csv index 6bbe492d56..c18741a394 100644 --- a/assets/events.csv +++ b/assets/events.csv @@ -1,9 +1,4 @@ Name,Priority,Start Date,End Date,City,Country,Virtual Option,Description,URL,Categories -Everything Open,2,2025-01-20,2025-01-22,Tarntanya,Australia,FALSE,Example description. Example description. Example description. Example description. Example description.,https://everythingopen.au/,Servers and Cloud Computing; AI; IoT -FOSDEM 25,2,2025-02-01,2025-02-02,Brussels,Belgium,FALSE,Example description. Example description. Example description. Example description. Example description.,https://fosdem.org/2025/,AI; Servers and Cloud Computing; IoT; Embedded and Microcontrollers -State of Open,2,2025-02-04,2025-02-05,London,United Kingdom,FALSE,Example description. Example description. Example description. Example description. Example description.,https://stateofopencon.com/,Servers and Cloud Computing; AI; IoT -Arm AI Innovation Day,2,2025-02-15,2025-02-15,Nairobi,Kenya,,Example description. Example description. Example description. Example description. Example description.,TBC,AI -Rust Nation,2,2025-02-19,2025-02-20,London,United Kingdom,FALSE,Example description. Example description. Example description. Example description. Example description.,https://www.rustnationuk.com/,Embedded and Microcontrollers; AI; IoT SOSS Policy Summit,2,2025-03-04,2025-03-04,Washington,United States,FALSE,"Hosted by the Open Source Security Foundation (OpenSSF), an initiative of the Linux Foundation, this event addresses the security challenges associated with Open Source Software (OSS).",https://events.linuxfoundation.org/openssf-policy-summit-dc/,Servers and Cloud Computing SCaLE,1,2025-03-06,2025-03-09,Pasadena,United States,FALSE,SCaLE is the largest community-run open-source and free software conference in North America. It is held annually in the greater Los Angeles area.,https://www.socallinuxexpo.org/scale/22x,Servers and Cloud Computing SUSECon,2,2025-03-10,2025-03-14,Orlando,United States,FALSE,Example description. Example description. Example description. Example description. Example description.,https://www.suse.com/susecon/,Servers and Cloud Computing; AI; IoT @@ -12,4 +7,4 @@ FOSSAsia,2,2025-03-13,2025-03-15,Bangkok,Thailand,TRUE,Example description. Exam NVIDIA GTC,1,2025-03-17,2025-03-21,San Jose,United States,TRUE,"Nvidia GTC is a global artificial intelligence conference for developers that brings together developers, engineers, researchers, inventors, and IT professionals. ",https://www.nvidia.com/gtc/,ML GDC,1,2025-03-17,2025-03-21,San Fransisco,United States,FALSE,"The Game Developers Conference (GDC) is the world's premier event for developers who make the games we love. GDC is the destination for creativity, innovation, and excellence.",https://gdconf.com/,"Mobile, Graphics, and Gaming" ATO AI,2,2025-03-17,2025-03-18,Durham,United States,,Example description. Example description. Example description. Example description. Example description.,https://allthingsopen.ai/,AI -KubeCon EU,2,2025-04-01,2025-04-04,London ,United Kingdom,TRUE,Example description. Example description. Example description. Example description. Example description.,https://events.linuxfoundation.org/kubecon-cloudnativecon-europe/,Servers and Cloud Computing +KubeCon EU,1,2025-04-01,2025-04-04,London,United Kingdom,TRUE,"Europe's Cloud Native Computing Foundation's flagship conference, this four-day event focuses on Kubernetes and cloud-native technologies, with keynotes, technical sessions and collaboration opportunities. ",https://events.linuxfoundation.org/kubecon-cloudnativecon-europe/,Servers and Cloud Computing From 68d78722f523e9edf9a3961dcd05a87f041287f1 Mon Sep 17 00:00:00 2001 From: Joe Stech <4088382+JoeStech@users.noreply.github.com> Date: Thu, 20 Feb 2025 08:56:13 -0700 Subject: [PATCH 13/15] change time to read to 30 --- .../copilot-extension-deployment/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md index 393285376c..930373ef71 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md @@ -1,7 +1,7 @@ --- title: Graviton Infrastructure for GitHub Copilot Extensions -minutes_to_complete: 20 +minutes_to_complete: 30 who_is_this_for: This is an advanced topic for software developers who want to learn how to deploy all necessary infrastructure on AWS for a GitHub Copilot Extension. From 2c806be080718e2a9fe376a7b24b23e0d9f1564e Mon Sep 17 00:00:00 2001 From: pareenaverma Date: Thu, 20 Feb 2025 23:55:32 +0000 Subject: [PATCH 14/15] Removed review from sme and kleidicv lp, removed draft from tinyML and added GH copilot LP to draft --- .../multiplying-matrices-with-sme2/_review.md | 45 ------------- .../introduction-to-tinyml-on-arm/_index.md | 4 -- .../android_opencv_kleidicv/_review.md | 64 ------------------- .../copilot-extension-deployment/_index.md | 4 ++ 4 files changed, 4 insertions(+), 113 deletions(-) delete mode 100644 content/learning-paths/cross-platform/multiplying-matrices-with-sme2/_review.md delete mode 100644 content/learning-paths/mobile-graphics-and-gaming/android_opencv_kleidicv/_review.md diff --git a/content/learning-paths/cross-platform/multiplying-matrices-with-sme2/_review.md b/content/learning-paths/cross-platform/multiplying-matrices-with-sme2/_review.md deleted file mode 100644 index 49682188ae..0000000000 --- a/content/learning-paths/cross-platform/multiplying-matrices-with-sme2/_review.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -review: - - questions: - question: > - How does SME2 accelerate matrix multiplication? - answers: - - The matrix multiplication operation is a sum of outer products. - - Quantum physics. - correct_answer: 1 - explanation: > - The matrix multiplication operation can be expressed as a sum of outer products, - which allows the SME engine to perform many multiplications at once. - - - questions: - question: > - Why is the ZA storage so important for SME2? - answers: - - It is infinite. - - It holds a 2D view of matrices. - correct_answer: 2 - explanation: > - The ZA storage offers a 2D view of part of a matrix, which is also known as a tile. SME can operate - on complete tiles, or on horizontal or vertical slices of the tiles, which is a useful - and often-used feature in numerous algorithms. ZA storage is finite and has the size SVL x SVL. - - - questions: - question: > - What are predicates? - answers: - - Parts of a sentence or clause containing a verb and stating something about the subject. - - Predicates select the active lanes in a vector operation. - - Predicates are another word for flags from the Processor Status Register (PSR). - correct_answer: 2 - explanation: > - SVE is a predicate-centric architecture. Predicates allow Vector Length Agnosticism (VLA), they support complex nested conditions and loops and reduce vector loop management overhead by allowing lane predication in vector operations. Predicates have their own dedicated registers. - - - -# ================================================================================ -# FIXED, DO NOT MODIFY -# ================================================================================ -title: "Review" # Always the same title -weight: 20 # Set to always be larger than the content in this path -layout: "learningpathall" # All files under learning paths have this same wrapper ---- diff --git a/content/learning-paths/embedded-and-microcontrollers/introduction-to-tinyml-on-arm/_index.md b/content/learning-paths/embedded-and-microcontrollers/introduction-to-tinyml-on-arm/_index.md index f244dab30e..d323fc0bf3 100644 --- a/content/learning-paths/embedded-and-microcontrollers/introduction-to-tinyml-on-arm/_index.md +++ b/content/learning-paths/embedded-and-microcontrollers/introduction-to-tinyml-on-arm/_index.md @@ -1,10 +1,6 @@ --- title: Introduction to TinyML on Arm using PyTorch and ExecuTorch -draft: true -cascade: - draft: true - minutes_to_complete: 40 who_is_this_for: This is an introductory topic for developers and data scientists new to Tiny Machine Learning (TinyML) who want to explore its potential using PyTorch and ExecuTorch. diff --git a/content/learning-paths/mobile-graphics-and-gaming/android_opencv_kleidicv/_review.md b/content/learning-paths/mobile-graphics-and-gaming/android_opencv_kleidicv/_review.md deleted file mode 100644 index 34de98efff..0000000000 --- a/content/learning-paths/mobile-graphics-and-gaming/android_opencv_kleidicv/_review.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -# ================================================================================ -# Edit -# ================================================================================ - -# Always 3 questions. Should try to test the reader's knowledge, and reinforce the key points you want them to remember. - # question: A one sentence question - # answers: The correct answers (from 2-4 answer options only). Should be surrounded by quotes. - # correct_answer: An integer indicating what answer is correct (index starts from 0) - # explanation: A short (1-3 sentence) explanation of why the correct answer is correct. Can add additional context if desired - - -review: - - questions: - question: > - What is the purpose of the `ImageProcessor` class? - answers: - - "To handle user interactions for the application." - - "To apply a selected image processing operation to a Mat object." - - "To manage and display performance metrics for image processing." - correct_answer: 2 - explanation: > - The `ImageProcessor` class is responsible for applying a specified image processing operation from the `ImageOperation` enum to a Mat object. - - - questions: - question: > - How does the `PerformanceMetrics` class compute the standard deviation of operation durations? - answers: - - "By finding the difference between the maximum and minimum durations." - - "By calculating the square root of the average of squared differences from the mean." - - "By dividing the total duration by the number of repetitions." - correct_answer: 2 - explanation: > - The `PerformanceMetrics` class computes the standard deviation by finding the mean of durations, calculating the squared differences from the mean, averaging these values, and taking the square root of the result. - - - questions: - question: > - What is the purpose of the `REPETITIONS` constant in `MainActivity`? - answers: - - "To set the number of times the user can retry loading an image." - - "To determine how many times an image operation is repeated for performance measurement." - - "To limit the maximum number of image processing operations the app supports." - correct_answer: 2 - explanation: > - The `REPETITIONS` constant specifies how many times an image operation is repeated to measure performance and gather statistical data. - - - questions: - question: > - What does the `convertBitmapToMat` method in `MainActivity` achieve? - answers: - - "It initializes OpenCV with the provided bitmap." - - "It converts a Bitmap object into a Mat object and prepares it for processing by changing its color space." - - "It displays the bitmap on the screen." - correct_answer: 2 - explanation: > - The `convertBitmapToMat` method converts a Bitmap to a Mat object using OpenCV utilities and changes its color space from RGBA to BGR, making it ready for further image processing. - -# ================================================================================ -# FIXED, DO NOT MODIFY -# ================================================================================ -title: "Review" # Always the same title -weight: 20 # Set to always be larger than the content in this path -layout: "learningpathall" # All files under learning paths have this same wrapper ---- diff --git a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md index 930373ef71..ec477120f8 100644 --- a/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/copilot-extension-deployment/_index.md @@ -1,6 +1,10 @@ --- title: Graviton Infrastructure for GitHub Copilot Extensions +draft: true +cascade: + draft: true + minutes_to_complete: 30 who_is_this_for: This is an advanced topic for software developers who want to learn how to deploy all necessary infrastructure on AWS for a GitHub Copilot Extension. From 0b716c7f6b5d5d482452972bb5bca1ac18f2c21f Mon Sep 17 00:00:00 2001 From: pareenaverma Date: Fri, 21 Feb 2025 00:02:31 +0000 Subject: [PATCH 15/15] Updated example LP overview --- .../cross-platform/_example-learning-path/overview.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/content/learning-paths/cross-platform/_example-learning-path/overview.md b/content/learning-paths/cross-platform/_example-learning-path/overview.md index 6fcc6f348c..9c695390e3 100644 --- a/content/learning-paths/cross-platform/_example-learning-path/overview.md +++ b/content/learning-paths/cross-platform/_example-learning-path/overview.md @@ -58,8 +58,6 @@ Learning Paths are about software development on Arm. Content is segmented into Learning Paths include only public information. Do not include confidential information, trade secrets, unannounced products, or any other information which should not be on a public website. -Do not use an AI tool to generate either content or code when creating a Learning Path or Install Guide. - ## Is there a way to ask about my Learning Path idea? You can use [GitHub Discussions](https://github.com/ArmDeveloperEcosystem/arm-learning-paths/discussions) to ask questions about your Learning Path idea. You may want to do this if you are unsure about the usefulness of your concept or think it might already be covered by other content. You can also use it to determine the best category for your Learning Path. It is possible that a Learning Path belongs to multiple categories, so use GitHub discussions to ask.